本文档的Copyleft归yfydz所有,使用GPL发布,可以自由拷贝,转载,转载时请保持文档的完整性,严禁用于任何商业用途。
msn: yfydz_no1@hotmail.com
来源:http://yfydz.cublog.cn
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 数据结构
/* include/net/act_api.h */
// 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; /* for backward compat(TCA_OLD_COMPAT) */
// 阶数
__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; /* TBD to match kind */
__u32 capab; /* capabilities includes 4 bit version */
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 初始化
/* net/sched/act_api.c */
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;
}
/* n->nlmsg_flags&NLM_F_CREATE
* */
switch (n->nlmsg_type) {
case RTM_NEWACTION:
// 新建
/* we are going to assume all other flags
* imply create only if it doesnt exist
* Note that CREATE | EXCL implies that
* but since we want avoid ambiguity (eg when flags
* is zero) then just set this
*/
// 是否是修改操作
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;
/* dump then free all the actions after update; inserted policy
* stays intact
* */
// 向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);
/* We dropped the RTNL semaphore in order to
* perform the module load. So, even if we
* succeeded in loading the module we have to
* tell the caller to replay the request. We
* indicate this using -EAGAIN.
*/
// 找到, 返回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;
/* backward compatibility for policer */
// 调用动作操作结构的初始化函数进行初始化
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;
/* module count goes up only when brand new policy is created
if it exists and is only bound to in a_o->init() then
ACT_P_CREATED is not returned (a zero is).
*/
// 如果初始化结果不是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 { /* delete */
// 否则是删除操作
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;
}
/* now do the delete */
// 释放动作, 注意这里的释放和下面的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 { /*FIXME: Remove later - catch insertion bugs*/
// 无清除操作, 直接断开链表, 释放节点
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;
}
...... 待续 ......