linux 内核邯山区strim,Linux内核中流量控制(22)

本文档的Copyleft归yfydz所有,使用GPL发布,可以自由拷贝,转载,转载时请保持文档的完整性,严禁用于任何商业用途。

msn: yfydz_no1@hotmail.com

来源:http://yfydz.cublog.cn

8. action动作操作

8.1 概述

tc action命令是用来定义数据包进行最终处理方法的命令, 其功能就象netfilter的target一样,

需要内核定义NET_CLS_ACT选项。

动作处理的基本api在net/sched/act_api.c中定义,

而各种动作方法在net/sched/act_*.c中定义。

8.2 数据结构

// TCF通用结构, 实际是用来描述动作的, 这是通用结构, 每种动作结构还有自己的

// 私有数据

struct tcf_common {

struct

tcf_common *tcfc_next;

u32 tcfc_index;

int tcfc_refcnt;

int tcfc_bindcnt;

u32 tcfc_capab;

int tcfc_action;

struct

tcf_t tcfc_tm;

struct

gnet_stats_basic tcfc_bstats;

struct

gnet_stats_queue tcfc_qstats;

struct

gnet_stats_rate_est tcfc_rate_est;

spinlock_t *tcfc_stats_lock;

spinlock_t tcfc_lock;

};

#define tcf_next common.tcfc_next

#define tcf_index common.tcfc_index

#define tcf_refcnt common.tcfc_refcnt

#define tcf_bindcnt common.tcfc_bindcnt

#define tcf_capab common.tcfc_capab

#define tcf_action common.tcfc_action

#define

tcf_tm common.tcfc_tm

#define tcf_bstats common.tcfc_bstats

#define tcf_qstats common.tcfc_qstats

#define tcf_rate_est common.tcfc_rate_est

#define

tcf_stats_lock common.tcfc_stats_lock

#define tcf_lock common.tcfc_lock

// action操作结果, 其实这是一个中间数据结构, 在操作过程中产生的

// 命令执行完其实也就释放了没必要一直保存

struct tc_action {

// 私有数据

void *priv;

// 操作结构

struct tc_action_ops *ops;

// 类型

__u32 type;

// 阶数

__u32 order;

// 动作链表下一项

struct tc_action *next;

};

#define TCA_CAP_NONE 0

// action操作结构, 实际就是定义目标操作, 通常每个匹配操作都由一个静态tcf_action_ops

// 结构定义, 作为一个内核模块, 初始化事登记系统的链表

struct tc_action_ops {

// 链表中的下一项

struct tc_action_ops *next;

struct tcf_hashinfo *hinfo;

// 名称

char kind[IFNAMSIZ];

__u32 type;

__u32

capab; struct

module *owner;

// 动作

int (*act)(struct sk_buff *, struct tc_action *, struct tcf_result

*);

// 获取统计参数

int (*get_stats)(struct sk_buff *, struct tc_action *);

// 输出

int (*dump)(struct sk_buff *, struct tc_action *, int, int);

// 清除

int (*cleanup)(struct tc_action *, int bind);

// 查找

int (*lookup)(struct tc_action *, u32);

// 初始化

int (*init)(struct rtattr *, struct rtattr *, struct tc_action *, int ,

int);

// 遍历

int (*walk)(struct sk_buff *, struct netlink_callback *, int, struct

tc_action *);

};

8.3 初始化

static int __init tc_action_init(void)

{

struct rtnetlink_link *link_p =

rtnetlink_links[PF_UNSPEC];

if (link_p) {

// 定义action操作处理函数

// 关于action的增加/删除/获取等操作

link_p[RTM_NEWACTION-RTM_BASE].doit

= tc_ctl_action;

link_p[RTM_DELACTION-RTM_BASE].doit

= tc_ctl_action;

link_p[RTM_GETACTION-RTM_BASE].doit

= tc_ctl_action;

link_p[RTM_GETACTION-RTM_BASE].dumpit

= tc_dump_action;

}

return 0;

}

8.4 filter控制

相关函数调用关系:

tc_ctl_action

-> tcf_action_add

-> tcf_action_init

-> tcf_action_init_1

-> tc_lookup_action_n

-> action_ops->init

-> tcf_add_notify

-> tcf_action_dump

-> tcf_action_dump_1

-> tcf_action_dump_old

-> a->ops->dump

-> tcf_action_gd

-> tca_action_flush

-> create_a

-> tcf_action_get_1

-> act_get_notify

-> tca_get_fill

-> tcf_action_dump

-> rtnl_unicast

-> tca_get_fill

-> tcf_action_destroy

8.4.1 编辑操作

// 用于增加, 修改, 删除, 获取动作结构

static int tc_ctl_action(struct sk_buff *skb, struct nlmsghdr *n,

void *arg)

{

// 属性配置参数

struct rtattr **tca = arg;

// 用户空间进程的pid

u32 pid = skb ? NETLINK_CB(skb).pid : 0;

int ret = 0, ovr = 0;

// 动作参数为空, 返回非法参数错误

if (tca[TCA_ACT_TAB-1] == NULL) {

printk("tc_ctl_action: received

NO action attribs\n");

return -EINVAL;

}

switch (n->nlmsg_type) {

case RTM_NEWACTION:

// 新建

// 是否是修改操作

if

(n->nlmsg_flags&NLM_F_REPLACE)

ovr =

1;

replay:

ret =

tcf_action_add(tca[TCA_ACT_TAB-1], n, pid, ovr);

// 如果操作结果是重来, 重新进行添加操作, 在相关动作的内核模块没插入内核时会出现这种情况

if (ret == -EAGAIN)

goto

replay;

break;

case RTM_DELACTION:

// 删除动作

ret =

tca_action_gd(tca[TCA_ACT_TAB-1], n, pid, RTM_DELACTION);

break;

case RTM_GETACTION:

// 获取动作

ret =

tca_action_gd(tca[TCA_ACT_TAB-1], n, pid, RTM_GETACTION);

break;

default:

BUG();

}

return ret;

}

8.4.2 增加

// 添加动作

static int

tcf_action_add(struct rtattr *rta, struct nlmsghdr *n, u32 pid, int

ovr)

{

int ret = 0;

struct tc_action *act;

struct tc_action *a;

u32 seq = n->nlmsg_seq;

// 根据选项信息进行TCF动作结构的初始化, 生成动作链表, 注意, 这个链表只是中间结果

act = tcf_action_init(rta, NULL, NULL, ovr, 0,

&ret);

if (act == NULL)

goto done;

// 向pid进程发送netlink通知消息

ret = tcf_add_notify(act, pid, seq,

RTM_NEWACTION, n->nlmsg_flags);

// 释放动作链表中的所有节点, 因为是中间结果, 没必要保存的

for (a = act; a; a = act) {

// 断开链表

act =

a->next;

// 释放动作的内存空间

kfree(a);

}

done:

return ret;

}

// 动作初始化

struct tc_action *tcf_action_init(struct rtattr *rta, struct rtattr

*est,

char *name, int ovr, int bind, int *err)

{

struct rtattr *tb[TCA_ACT_MAX_PRIO+1];

struct tc_action *head = NULL, *act, *act_prev =

NULL;

int i;

// 解析动作参数, TCA_ACT_MAX_PRIO为32

if (rtattr_parse_nested(tb, TCA_ACT_MAX_PRIO,

rta) < 0) {

*err = -EINVAL;

return head;

}

// 循环所有参数项

for (i=0; i < TCA_ACT_MAX_PRIO

&& tb[i]; i++) {

// 初始化一个动作结构

act = tcf_action_init_1(tb[i],

est, name, ovr, bind, err);

// 失败的话返回

if (act == NULL)

goto

err;

// 动作顺序

act->order =

i+1;

// 加入一head为头指针的动作链表

if (head == NULL)

head =

act;

else

act_prev->next

= act;

act_prev = act;

}

// 返回链表头指针

return head;

err:

// 错误处理, 释放当前链表所有节点

if (head != NULL)

tcf_action_destroy(head,

bind);

return NULL;

}

// 初始化单个动作

struct tc_action *tcf_action_init_1(struct rtattr *rta, struct

rtattr *est,

char *name, int ovr, int bind, int *err)

{

struct tc_action *a;

struct tc_action_ops *a_o;

char act_name[IFNAMSIZ];

struct rtattr *tb[TCA_ACT_MAX+1];

struct rtattr *kind;

*err = -EINVAL;

if (name == NULL) {

// 名称参数为空, 从配置参数中提取名称

if (rtattr_parse_nested(tb,

TCA_ACT_MAX, rta) < 0)

goto

err_out;

// 动作名称

kind =

tb[TCA_ACT_KIND-1];

if (kind == NULL)

goto

err_out;

// 名称复制

if (rtattr_strlcpy(act_name,

kind, IFNAMSIZ) >= IFNAMSIZ)

goto

err_out;

} else {

// 名称复制

if (strlcpy(act_name, name,

IFNAMSIZ) >= IFNAMSIZ)

goto

err_out;

}

// 根据名称查找动作操作结构

a_o = tc_lookup_action_n(act_name);

if (a_o == NULL) {

#ifdef CONFIG_KMOD

// 如果没找到, 进行模块请求操作, 动态插入和名称对应的动作模块

rtnl_unlock();

request_module("act_%s",

act_name);

rtnl_lock();

// 重新查找动作操作结构

a_o =

tc_lookup_action_n(act_name);

// 找到, 返回EAGAIN错误, 重新操作

if (a_o != NULL) {

*err =

-EAGAIN;

goto

err_mod;

}

#endif

// 否则操作失败, 没有该类型的动作操作

*err = -ENOENT;

goto err_out;

}

*err = -ENOMEM;

// 分配动作空间

a = kzalloc(sizeof(*a), GFP_KERNEL);

if (a == NULL)

goto err_mod;

// 调用动作操作结构的初始化函数进行初始化

if (name == NULL)

*err =

a_o->init(tb[TCA_ACT_OPTIONS-1], est, a, ovr,

bind);

else

*err =

a_o->init(rta, est, a, ovr, bind);

if (*err < 0)

goto err_free;

// 如果初始化结果不是ACT_P_CREATED, 减少模块计数

if (*err != ACT_P_CREATED)

module_put(a_o->owner);

// 操作结构指针复制

a->ops = a_o;

*err = 0;

return a;

err_free:

kfree(a);

err_mod:

module_put(a_o->owner);

err_out:

return NULL;

}

// 增加操作通告

static int tcf_add_notify(struct tc_action *a, u32 pid, u32 seq,

int event,

u16 flags)

{

struct tcamsg *t;

struct nlmsghdr *nlh;

struct sk_buff *skb;

struct rtattr *x;

unsigned char *b;

int err = 0;

// 分配数据包用于发送到用户空间进程

skb = alloc_skb(NLMSG_GOODSIZE,

GFP_KERNEL);

if (!skb)

return -ENOBUFS;

// 数据缓存起始点

b = (unsigned char

*)skb->tail;

// 构造netlink头

nlh = NLMSG_NEW(skb, pid, seq, event, sizeof(*t),

flags);

// TC动作信息头

t = NLMSG_DATA(nlh);

t->tca_family = AF_UNSPEC;

t->tca__pad1 = 0;

t->tca__pad2 = 0;

x = (struct rtattr*)

skb->tail;

RTA_PUT(skb, TCA_ACT_TAB, 0, NULL);

// 输出动作参数

if (tcf_action_dump(skb, a, 0, 0)

< 0)

goto rtattr_failure;

// rtnetlink信息长度

x->rta_len =

skb->tail - (u8*)x;

// netlink信息长度

nlh->nlmsg_len =

skb->tail - b;

NETLINK_CB(skb).dst_group = RTNLGRP_TC;

// 发送消息

err = rtnetlink_send(skb, pid, RTNLGRP_TC,

flags&NLM_F_ECHO);

if (err > 0)

err = 0;

return err;

rtattr_failure:

nlmsg_failure:

kfree_skb(skb);

return -1;

}

// 动作参数输出

int

tcf_action_dump(struct sk_buff *skb, struct tc_action *act, int

bind, int ref)

{

struct tc_action *a;

int err = -EINVAL;

unsigned char *b =

skb->tail;

struct rtattr *r ;

// 遍历动作链表

while ((a = act) != NULL) {

// 当前数据末尾

r = (struct rtattr*)

skb->tail;

act =

a->next;

RTA_PUT(skb,

a->order, 0, NULL);

// 填充动作结构参数

err = tcf_action_dump_1(skb, a,

bind, ref);

if (err <

0)

goto

errout;

// 该rtnetlink消息长度

r->rta_len =

skb->tail - (u8*)r;

}

return 0;

rtattr_failure:

err = -EINVAL;

errout:

skb_trim(skb, b -

skb->data);

return err;

}

// 输出单一动作结构参数

int

tcf_action_dump_1(struct sk_buff *skb, struct tc_action *a, int

bind, int ref)

{

int err = -EINVAL;

unsigned char *b =

skb->tail;

struct rtattr *r;

// 如果没有动作操作结构或操作结构中无dump输出函数, 返回失败

if (a->ops == NULL ||

a->ops->dump == NULL)

return err;

RTA_PUT(skb, TCA_KIND, IFNAMSIZ,

a->ops->kind);

// 输出统计参数

if (tcf_action_copy_stats(skb, a, 0))

goto rtattr_failure;

r = (struct rtattr*)

skb->tail;

RTA_PUT(skb, TCA_OPTIONS, 0, NULL);

// 动作参数输出

if ((err = tcf_action_dump_old(skb, a, bind,

ref)) > 0) {

r->rta_len =

skb->tail - (u8*)r;

return err;

}

rtattr_failure:

skb_trim(skb, b -

skb->data);

return -1;

}

// 调用动作操作结构的dump函数输出动作参数信息

int

tcf_action_dump_old(struct sk_buff *skb, struct tc_action *a, int

bind, int ref)

{

int err = -EINVAL;

if (a->ops == NULL ||

a->ops->dump == NULL)

return err;

return

a->ops->dump(skb, a, bind,

ref);

}

8.4.3 获取/删除动作

static int

tca_action_gd(struct rtattr *rta, struct nlmsghdr *n, u32 pid, int

event)

{

int i, ret = 0;

struct rtattr *tb[TCA_ACT_MAX_PRIO+1];

struct tc_action *head = NULL, *act, *act_prev =

NULL;

// 选项参数解析

if (rtattr_parse_nested(tb, TCA_ACT_MAX_PRIO,

rta) < 0)

return -EINVAL;

if (event == RTM_DELACTION

&&

n->nlmsg_flags&NLM_F_ROOT) {

// 删除根节点操作

if (tb[0] != NULL

&& tb[1] == NULL)

// 删除全部动作

return

tca_action_flush(tb[0], n, pid);

}

// 遍历所有参数

for (i=0; i < TCA_ACT_MAX_PRIO

&& tb[i]; i++) {

// 获取一个动作

act = tcf_action_get_1(tb[i],

n, pid, &ret);

if (act == NULL)

goto

err;

act->order =

i+1;

// 添加到以head为头的链表中

if (head == NULL)

head =

act;

else

act_prev->next

= act;

act_prev = act;

}

if (event == RTM_GETACTION)

// 如果是获取动作, 发送获取的动作链表到用户空间进程

ret = act_get_notify(pid, n,

head, event);

else {

// 否则是删除操作

struct sk_buff *skb;

// 分配一个skb数据包准备发送删除通告

skb = alloc_skb(NLMSG_GOODSIZE,

GFP_KERNEL);

if (!skb) {

ret =

-ENOBUFS;

goto

err;

}

// TC动作信息填充到skb数据包中

if (tca_get_fill(skb, head,

pid, n->nlmsg_seq, 0, event,

0, 1) <= 0) {

// 填充失败返回

kfree_skb(skb);

ret =

-EINVAL;

goto

err;

}

// 释放动作, 注意这里的释放和下面的cleanup_a()不同, 操作更复杂

tcf_action_destroy(head,

0);

// 发送数据包到pid进程

ret = rtnetlink_send(skb, pid,

RTNLGRP_TC,

n->nlmsg_flags&NLM_F_ECHO);

if (ret >

0)

return

0;

return ret;

}

err:

// 删除head链表, 只是简单释放动态内存空间

cleanup_a(head);

return ret;

}

// 删除全部动作

static int tca_action_flush(struct rtattr *rta, struct nlmsghdr *n,

u32 pid)

{

struct sk_buff *skb;

unsigned char *b;

struct nlmsghdr *nlh;

struct tcamsg *t;

struct netlink_callback dcb;

struct rtattr *x;

struct rtattr *tb[TCA_ACT_MAX+1];

struct rtattr *kind;

// 先分配一个动作结构, 阶数为0, 这个节点只是用来辅助操作用的, 其实可以是一个结构

// 只是为了减少所用的堆栈空间

struct tc_action *a = create_a(0);

int err = -EINVAL;

if (a == NULL) {

printk("tca_action_flush:

couldnt create tc_action\n");

return err;

}

// 分配数据包

skb = alloc_skb(NLMSG_GOODSIZE,

GFP_KERNEL);

if (!skb) {

printk("tca_action_flush:

failed skb alloc\n");

kfree(a);

return -ENOBUFS;

}

// 数据缓冲区起始点

b = (unsigned char

*)skb->tail;

// 解析参数

if (rtattr_parse_nested(tb, TCA_ACT_MAX, rta)

< 0)

goto err_out;

// 动作类型

kind = tb[TCA_ACT_KIND-1];

// 根据名称查找动作类型

a->ops =

tc_lookup_action(kind);

if (a->ops == NULL)

goto err_out;

// 填充netlink数据头

nlh = NLMSG_PUT(skb, pid,

n->nlmsg_seq, RTM_DELACTION, sizeof(*t));

t = NLMSG_DATA(nlh);

t->tca_family = AF_UNSPEC;

t->tca__pad1 = 0;

t->tca__pad2 = 0;

x = (struct rtattr *)

skb->tail;

RTA_PUT(skb, TCA_ACT_TAB, 0, NULL);

// 调用动作操作结构的遍历操作函数进行删除, 其实最终调用的是tcf_del_walker

err =

a->ops->walk(skb,

&dcb, RTM_DELACTION, a);

if (err < 0)

goto rtattr_failure;

// 实际数据长度

x->rta_len =

skb->tail - (u8 *) x;

nlh->nlmsg_len =

skb->tail - b;

nlh->nlmsg_flags |=

NLM_F_ROOT;

module_put(a->ops->owner);

// 释放a节点

kfree(a);

// 发送数据包

err = rtnetlink_send(skb, pid, RTNLGRP_TC,

n->nlmsg_flags&NLM_F_ECHO);

if (err > 0)

return 0;

return err;

rtattr_failure:

nlmsg_failure:

module_put(a->ops->owner);

err_out:

kfree_skb(skb);

kfree(a);

return err;

}

// 填充动作参数到数据包

static int

tca_get_fill(struct sk_buff *skb, struct tc_action *a, u32 pid, u32

seq,

u16 flags, int event, int bind, int ref)

{

struct tcamsg *t;

struct nlmsghdr *nlh;

unsigned char *b =

skb->tail;

struct rtattr *x;

// 构造netlink数据头

nlh = NLMSG_NEW(skb, pid, seq, event, sizeof(*t),

flags);

// TC动作信息

t = NLMSG_DATA(nlh);

t->tca_family = AF_UNSPEC;

t->tca__pad1 = 0;

t->tca__pad2 = 0;

// 属性参数结构指针

x = (struct rtattr*)

skb->tail;

RTA_PUT(skb, TCA_ACT_TAB, 0, NULL);

// 动作结构输出

if (tcf_action_dump(skb, a, bind, ref)

< 0)

goto rtattr_failure;

// 数据长度

x->rta_len =

skb->tail - (u8*)x;

// netlink消息长度

nlh->nlmsg_len =

skb->tail - b;

return skb->len;

rtattr_failure:

nlmsg_failure:

skb_trim(skb, b -

skb->data);

return -1;

}

// 动作结构释放

void tcf_action_destroy(struct tc_action *act, int bind)

{

struct tc_action *a;

// 遍历链表

for (a = act; a; a = act) {

if (a->ops

&&

a->ops->cleanup) {

// 执行操作结构中的清除操作,

if

(a->ops->cleanup(a, bind) ==

ACT_P_DELETED)

module_put(a->ops->owner);

// 断开节点

act =

act->next;

// 释放节点

kfree(a);

} else {

// 无清除操作, 直接断开链表, 释放节点

printk("tcf_action_destroy:

BUG? destroying NULL ops\n");

act =

act->next;

kfree(a);

}

}

}

8.5 filter输出

static int

tc_dump_action(struct sk_buff *skb, struct netlink_callback

*cb)

{

struct nlmsghdr *nlh;

// 数据包缓冲区起始点

unsigned char *b =

skb->tail;

struct rtattr *x;

struct tc_action_ops *a_o;

// 这个动作参数只是一个辅助作用

struct tc_action a;

int ret = 0;

struct tcamsg *t = (struct tcamsg *)

NLMSG_DATA(cb->nlh);

// 查找动作类型名称

struct rtattr *kind =

find_dump_kind(cb->nlh);

if (kind == NULL) {

printk("tc_dump_action: action

bad kind\n");

return 0;

}

// 根据类型名称查找动作操作结构

a_o = tc_lookup_action(kind);

if (a_o == NULL) {

return 0;

}

// 结构清零

memset(&a, 0, sizeof(struct

tc_action));

// 动作操作结构赋值

a.ops = a_o;

// 如果该操作结构中没有遍历函数, 失败

if (a_o->walk == NULL) {

printk("tc_dump_action: %s

!capable of dumping table\n", a_o->kind);

goto rtattr_failure;

}

// 填写netlink头数据

nlh = NLMSG_PUT(skb,

NETLINK_CB(cb->skb).pid,

cb->nlh->nlmsg_seq,

cb->nlh->nlmsg_type,

sizeof(*t));

t = NLMSG_DATA(nlh);

t->tca_family = AF_UNSPEC;

t->tca__pad1 = 0;

t->tca__pad2 = 0;

x = (struct rtattr *)

skb->tail;

RTA_PUT(skb, TCA_ACT_TAB, 0, NULL);

// 遍历所有action节点执行GET操作

ret = a_o->walk(skb, cb,

RTM_GETACTION, &a);

if (ret < 0)

goto rtattr_failure;

// 操作成功, 设置数据包长度

if (ret > 0) {

x->rta_len =

skb->tail - (u8 *) x;

ret =

skb->len;

} else

skb_trim(skb, (u8*)x -

skb->data);

// 填充的数据长度

nlh->nlmsg_len =

skb->tail - b;

if (NETLINK_CB(cb->skb).pid

&& ret)

nlh->nlmsg_flags

|= NLM_F_MULTI;

// 减少模块引用, 因为在tc_lookup_action时增加了引用

module_put(a_o->owner);

return skb->len;

rtattr_failure:

nlmsg_failure:

module_put(a_o->owner);

skb_trim(skb, b -

skb->data);

return skb->len;

}

8.6 其他相关函数

8.6.1 遍历

// 通用遍历函数, 在一些动作操作结构中的walk函数就用这个函数

// 不过只进行删除和输出两种遍历操作

int tcf_generic_walker(struct sk_buff *skb, struct netlink_callback

*cb,

int type, struct tc_action *a)

{

// 哈希信息结构

struct tcf_hashinfo *hinfo =

a->ops->hinfo;

// 根据动作类型进行不同的遍历操作

if (type == RTM_DELACTION) {

// 删除遍历

return tcf_del_walker(skb, a,

hinfo);

} else if (type == RTM_GETACTION) {

// 输出遍历

return tcf_dump_walker(skb, cb,

a, hinfo);

} else {

// 其他是非法参数

printk("tcf_generic_walker:

unknown action %d\n", type);

return -EINVAL;

}

}

// 输出遍历

static int tcf_dump_walker(struct sk_buff *skb, struct

netlink_callback *cb,

struct tc_action *a, struct tcf_hashinfo *hinfo)

{

struct tcf_common *p;

int err = 0, index = -1,i = 0, s_i = 0, n_i =

0;

struct rtattr *r ;

read_lock(hinfo->lock);

// 起始

s_i = cb->args[0];

// 遍历所有哈希表

for (i = 0; i <

(hinfo->hmask + 1); i++) {

// 链表头

p =

hinfo->htab[tcf_hash(i,

hinfo->hmask)];

// 遍历链表

for (; p; p =

p->tcfc_next) {

index++;

// 是否是要跳过的数

if (index

< s_i)

continue;

// 将common节点赋值为动作结构的私有数据

a->priv

= p;

a->order

= n_i;

// 数据缓冲区起始点

r = (struct

rtattr*) skb->tail;

RTA_PUT(skb,

a->order, 0, NULL);

// 填写一个动作结构参数

err =

tcf_action_dump_1(skb, a, 0, 0);

if (err

< 0) {

/ 操作失败, 中断循环

index--;

skb_trim(skb,

(u8*)r - skb->data);

goto

done;

}

// 该信息长度

r->rta_len

= skb->tail - (u8*)r;

// 统计计数

n_i++;

if (n_i

>= TCA_ACT_MAX_PRIO)

goto

done;

}

}

done:

read_unlock(hinfo->lock);

// 增加统计数

if (n_i)

cb->args[0] +=

n_i;

return n_i;

rtattr_failure:

skb_trim(skb, (u8*)r -

skb->data);

goto done;

}

// 删除遍历

static int tcf_del_walker(struct sk_buff *skb, struct tc_action

*a,

struct tcf_hashinfo *hinfo)

{

struct tcf_common *p, *s_p;

struct rtattr *r ;

int i= 0, n_i = 0;

// 数据缓冲区起始点

r = (struct rtattr*)

skb->tail;

// 填写动作节点的order和名称

RTA_PUT(skb, a->order, 0,

NULL);

RTA_PUT(skb, TCA_KIND, IFNAMSIZ,

a->ops->kind);

// 遍历所有common哈希表

for (i = 0; i <

(hinfo->hmask + 1); i++) {

// 链表头

p =

hinfo->htab[tcf_hash(i,

hinfo->hmask)];

// 遍历链表

while (p != NULL) {

s_p =

p->tcfc_next;

// 释放common节点

if

(ACT_P_DELETED == tcf_hash_release(p, 0, hinfo))

module_put(a->ops->owner);

// 计数

n_i++;

p =

s_p;

}

}

// 删除的节点数

RTA_PUT(skb, TCA_FCNT, 4,

&n_i);

// 数据长度

r->rta_len =

skb->tail - (u8*)r;

return n_i;

rtattr_failure:

skb_trim(skb, (u8*)r -

skb->data);

return -EINVAL;

}

8.6.2 tcf_common结构相关操作

tcf_common结构是作为动作结构的私有数据(priv), 所有common结构都保存为哈希表, 是TC动作的具体实现,

所有common结构节点是通过哈希表链接在一起的.

// 根据索引号index查找common节点

struct tcf_common *tcf_hash_lookup(u32 index, struct tcf_hashinfo

*hinfo)

{

struct tcf_common *p;

read_lock(hinfo->lock);

// 遍历相应的哈希链表

for (p =

hinfo->htab[tcf_hash(index,

hinfo->hmask)]; p;

p = p->tcfc_next) {

// 如果索引号相同则返回之

if

(p->tcfc_index == index)

break;

}

read_unlock(hinfo->lock);

return p;

}

EXPORT_SYMBOL(tcf_hash_lookup);

// 获取新索引值

u32 tcf_hash_new_index(u32 *idx_gen, struct tcf_hashinfo

*hinfo)

{

// 起始值

u32 val = *idx_gen;

do {

// 数组增加

if (++val == 0)

val =

1;

// 根据索引号查找common节点, 找到的话继续循环知道找到没有被使用的节点

// 这有可能会死循环, 就是已经分配了4G个common节点的情况

} while (tcf_hash_lookup(val, hinfo));

// 返回找到的索引号

return (*idx_gen = val);

}

EXPORT_SYMBOL(tcf_hash_new_index);

// 查找common结构作为动作结构的私有数据

// 返回1表操作成功, 0表示失败

int tcf_hash_search(struct tc_action *a, u32 index)

{

struct tcf_hashinfo *hinfo =

a->ops->hinfo;

// 根据索引值查找common结构

struct tcf_common *p = tcf_hash_lookup(index,

hinfo);

if (p) {

// 找到, 将common结构作为动作结构的私有数据

a->priv =

p;

return 1;

}

return 0;

}

EXPORT_SYMBOL(tcf_hash_search);

// 根据index获取common结构, 返回NULL表示失败

struct tcf_common *tcf_hash_check(u32 index, struct tc_action *a,

int bind,

struct tcf_hashinfo *hinfo)

{

struct tcf_common *p = NULL;

// 如果索引号非0, 查找相应的common节点

if (index && (p =

tcf_hash_lookup(index, hinfo)) != NULL) {

// 找到

if (bind) {

// 绑定操作, 增加引用数

p->tcfc_bindcnt++;

p->tcfc_refcnt++;

}

// 将common结构作为动作结构的私有数据

a->priv =

p;

}

// 返回common结构

return p;

}

EXPORT_SYMBOL(tcf_hash_check);

// 生成新common节点

struct tcf_common *tcf_hash_create(u32 index, struct rtattr *est,

struct tc_action *a, int size, int bind, u32 *idx_gen, struct

tcf_hashinfo *hinfo)

{

// 分配空间

struct tcf_common *p = kzalloc(size,

GFP_KERNEL);

if (unlikely(!p))

return p;

// 索引数初始化为1

p->tcfc_refcnt = 1;

// 如果要绑定, 绑定数也初始化为1

if (bind)

p->tcfc_bindcnt

= 1;

spin_lock_init(&p->tcfc_lock);

// 统计锁

p->tcfc_stats_lock =

&p->tcfc_lock;

// 索引数

p->tcfc_index = index ? index :

tcf_hash_new_index(idx_gen, hinfo);

// 生成时间

p->tcfc_tm.install =

jiffies;

// 上次使用时间

p->tcfc_tm.lastuse =

jiffies;

#ifdef CONFIG_NET_ESTIMATOR

// 初始化估计器结构

if (est)

gen_new_estimator(&p->tcfc_bstats,

&p->tcfc_rate_est,

p->tcfc_stats_lock, est);

#endif

// 将该common节点作为动作结构的私有数据

a->priv = (void *) p;

return p;

}

EXPORT_SYMBOL(tcf_hash_create);

// 插入common节点

void tcf_hash_insert(struct tcf_common *p, struct tcf_hashinfo

*hinfo)

{

// 计算哈希数

unsigned int h =

tcf_hash(p->tcfc_index,

hinfo->hmask);

write_lock_bh(hinfo->lock);

// 将新节点插头到链表头作为头节点

p->tcfc_next =

hinfo->htab[h];

hinfo->htab[h] = p;

write_unlock_bh(hinfo->lock);

}

EXPORT_SYMBOL(tcf_hash_insert);

// 释放

void tcf_hash_destroy(struct tcf_common *p, struct tcf_hashinfo

*hinfo)

{

// 计算哈希数

unsigned int h =

tcf_hash(p->tcfc_index,

hinfo->hmask);

struct tcf_common **p1p;

// 遍历哈希数指定的链表

for (p1p =

&hinfo->htab[h]; *p1p; p1p =

&(*p1p)->tcfc_next) {

// 比较结构地址

if (*p1p == p) {

// 找到

write_lock_bh(hinfo->lock);

// 从链表中断开

*p1p =

p->tcfc_next;

write_unlock_bh(hinfo->lock);

#ifdef CONFIG_NET_ESTIMATOR

// 释放估计器

gen_kill_estimator(&p->tcfc_bstats,

&p->tcfc_rate_est);

#endif

// 释放空间

kfree(p);

return;

}

}

BUG_TRAP(0);

}

EXPORT_SYMBOL(tcf_hash_destroy);

// TCF哈希信息释放

int tcf_hash_release(struct tcf_common *p, int bind,

struct tcf_hashinfo *hinfo)

{

int ret = 0;

if (p) {

// 如果已经是绑定的, 减少绑定数

if (bind)

p->tcfc_bindcnt--;

// 引用数减

p->tcfc_refcnt--;

// 如果绑定数和引用数都减到0了, 释放common节点

if (p->tcfc_bindcnt

<= 0 &&

p->tcfc_refcnt <= 0) {

tcf_hash_destroy(p,

hinfo);

ret =

1;

}

}

return ret;

}

8.6.3 登记/撤销

// 登记

int tcf_register_action(struct tc_action_ops *act)

{

struct tc_action_ops *a, **ap;

write_lock(&act_mod_lock);

// 遍历action操作链表, 这个链表中的动作操作结构应该都是静态量, 不是动态分配的

for (ap = &act_base; (a = *ap) !=

NULL; ap = &a->next) {

// 比较动作的类型或名称是否相同

if (act->type ==

a->type || (strcmp(act->kind,

a->kind) == 0)) {

// 相同的话表示已经登记过了

write_unlock(&act_mod_lock);

return

-EEXIST;

}

}

// 将新节点添加到链表末尾

act->next = NULL;

*ap = act;

write_unlock(&act_mod_lock);

return 0;

}

// 撤销

int tcf_unregister_action(struct tc_action_ops *act)

{

struct tc_action_ops *a, **ap;

int err = -ENOENT;

write_lock(&act_mod_lock);

// 遍历action操作链表, 直接比较地址

for (ap = &act_base; (a = *ap) !=

NULL; ap = &a->next)

if (a == act)

break;

if (a) {

// 如果找到

// 从链表中断开

*ap =

a->next;

a->next =

NULL;

err = 0;

}

write_unlock(&act_mod_lock);

return err;

}

...... 待续 ......

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值