本文档的Copyleft归yfydz所有,使用GPL发布,可以自由拷贝,转载,转载时请保持文档的完整性,严禁用于任何商业用途。
msn: yfydz_no1@hotmail.com
来源:http://yfydz.cublog.cn
6. 类别操作
6.1 概述
类别操作是通过tc class命令来完成的, 当网卡使用的流控算法是可分类的(如HTB, CBQ等)时候使用,
功能是对Qdisc根节点进行划分, 定义出分类树, 同时可定义每个类别的流量限制参数,但具体那些数据属于哪一类则是通过tc
filter命令来实现。
分类举例,以下命令在eth0上设置HTB流控,设置了3个类别,分别定义了各个类别的流量限制:
tc qdisc add dev eth0 root handle 1: htb default 12
tc class add dev eth0 parent 1: classid 1:1 htb rate 100kbps ceil
100kbps
tc class add dev eth0 parent 1:1 classid 1:10 htb rate 30kbps ceil
100kbps
tc class add dev eth0 parent 1:1 classid 1:11 htb rate 10kbps ceil
100kbps
tc class add dev eth0 parent 1:1 classid 1:12 htb rate 60kbps ceil
100kbps
类别操作的具体实现实际是通过Qdisc的类别操作来完成的, 下面的处理仅仅是一个接口处理而已,
而具体的Qdisc类别操作函数已经在分析Qdisc时介绍了, 所以也没有引入新的数据结构。
6.2 初始化
前面5.15.1节中的初始化处理已经包括了类别的初始化:
......
// class操作, 也就是对应tc class add/delete/modify/get等操作
link_p[RTM_NEWTCLASS-RTM_BASE].doit
= tc_ctl_tclass;
link_p[RTM_DELTCLASS-RTM_BASE].doit
= tc_ctl_tclass;
link_p[RTM_GETTCLASS-RTM_BASE].doit
= tc_ctl_tclass;
link_p[RTM_GETTCLASS-RTM_BASE].dumpit
= tc_dump_tclass;
......
6.3 类别控制操作
static int tc_ctl_tclass(struct sk_buff *skb, struct nlmsghdr
*n, void *arg)
{
struct tcmsg *tcm = NLMSG_DATA(n);
struct rtattr **tca = arg;
struct net_device *dev;
struct Qdisc *q = NULL;
struct Qdisc_class_ops *cops;
unsigned long cl = 0;
unsigned long new_cl;
// parent id
u32 pid = tcm->tcm_parent;
// class id
u32 clid = tcm->tcm_handle;
// qdisc id: 初始化位类别id的高16位
u32 qid = TC_H_MAJ(clid);
int err;
// 根据TC信息中的网卡索引值查找网卡
if ((dev =
__dev_get_by_index(tcm->tcm_ifindex)) == NULL)
return -ENODEV;
if (pid != TC_H_ROOT) {
// parent id非根节点的情况
u32 qid1 = TC_H_MAJ(pid);
if (qid
&& qid1) {
if (qid !=
qid1)
return
-EINVAL;
} else if (qid1) {
qid =
qid1;
} else if (qid == 0)
qid =
dev->qdisc_sleeping->handle;
if (pid)
pid =
TC_H_MAKE(qid, pid);
} else {
// 为根节点, 如果当前qid为0, 更新为设备的qdisc_sleeping的handle
if (qid == 0)
qid =
dev->qdisc_sleeping->handle;
}
// 根据qid查找该dev上的Qdisc指针, 找不到的话返回失败
if ((q = qdisc_lookup(dev, qid)) == NULL)
return -ENOENT;
// 获取Qdisc的类别操作指针
cops =
q->ops->cl_ops;
// 如果Qdisc是非分类的, 类别操作结构指针位空, 返回失败
if (cops == NULL)
return -EINVAL;
// 生成合法的类别ID
if (clid == 0) {
if (pid == TC_H_ROOT)
clid =
qid;
} else
clid = TC_H_MAKE(qid,
clid);
// 如果clid非0, 调用get函数获取该类别, 增加类别的引用计数
// cl虽然定义是unsigned long, 但实际是个指针的数值
if (clid)
cl =
cops->get(q, clid);
if (cl == 0) {
// 类别为空
err = -ENOENT;
// 如果netlink命令不是新建类别的话, 返回错误
if
(n->nlmsg_type != RTM_NEWTCLASS ||
!(n->nlmsg_flags&NLM_F_CREATE))
goto
out;
} else {
// 获取类别成功, 根据netlink命令类型进行相关操作
switch
(n->nlmsg_type) {
case
RTM_NEWTCLASS: // 新建class
err =
-EEXIST;
// 如果设置了互斥标志, 返回错误, 因为现在该class已经存在
if
(n->nlmsg_flags&NLM_F_EXCL)
goto
out;
break;
case RTM_DELTCLASS:
// 删除class
err =
cops->delete(q, cl);
if (err ==
0)
tclass_notify(skb,
n, q, cl, RTM_DELTCLASS);
goto
out;
case RTM_GETTCLASS:
// 获取class信息, 进行class通知操作
err =
tclass_notify(skb, n, q, cl, RTM_NEWTCLASS);
goto
out;
default:
err =
-EINVAL;
goto
out;
}
}
new_cl = cl;
// 不论是新建还是修改class参数, 都是调用类别操作结构的change函数
err = cops->change(q, clid, pid,
tca, &new_cl);
// 操作成功, 进行class通知操作
if (err == 0)
tclass_notify(skb, n, q,
new_cl, RTM_NEWTCLASS);
out:
if (cl)
cops->put(q,
cl);
return err;
}
// 类别通知处理, 向用户层发送消息数据
static int tclass_notify(struct sk_buff *oskb, struct nlmsghdr
*n,
struct Qdisc *q, unsigned long cl, int event)
{
struct sk_buff *skb;
// 从老数据包中查找通信进程的pid, 否则发送给所有打开netlink接口的进程
u32 pid = oskb ? NETLINK_CB(oskb).pid : 0;
// 分配数据包
skb = alloc_skb(NLMSG_GOODSIZE,
GFP_KERNEL);
if (!skb)
return -ENOBUFS;
// 填充class参数
if (tc_fill_tclass(skb, q, cl, pid,
n->nlmsg_seq, 0, event) < 0) {
kfree_skb(skb);
return -EINVAL;
}
// 通过rtnetlink发送数据包, 标志位ECHO包
return rtnetlink_send(skb, pid, RTNLGRP_TC,
n->nlmsg_flags&NLM_F_ECHO);
}
6.4 TC类输出
// 参数输出所用的临时数据结构
struct qdisc_dump_args
{
struct qdisc_walker w;
struct sk_buff *skb;
struct netlink_callback *cb;
};
// 类别输出
static int tc_dump_tclass(struct sk_buff *skb, struct
netlink_callback *cb)
{
int t;
int s_t;
struct net_device *dev;
struct Qdisc *q;
struct tcmsg *tcm = (struct
tcmsg*)NLMSG_DATA(cb->nlh);
struct qdisc_dump_args arg;
// 输入数据长度检查
if
(cb->nlh->nlmsg_len <
NLMSG_LENGTH(sizeof(*tcm)))
return 0;
// 查找网卡设备
if ((dev =
dev_get_by_index(tcm->tcm_ifindex)) == NULL)
return 0;
// s_t: 起始class索引
s_t = cb->args[0];
t = 0;
read_lock(&qdisc_tree_lock);
// 遍历设备的Qdisc链表
list_for_each_entry(q,
&dev->qdisc_list, list) {
// 当前索引号小于起始索引号, 或者当前Qdisc是非分类的,
// 或者句柄handle不匹配, 跳过
if (t < s_t ||
!q->ops->cl_ops ||
(tcm->tcm_parent
&&
TC_H_MAJ(tcm->tcm_parent) !=
q->handle)) {
t++;
continue;
}
// 索引号超过了起始索引号, 将从数组1号开始的数据缓冲区清零
if (t >
s_t)
memset(&cb->args[1],
0,
sizeof(cb->args)-sizeof(cb->args[0]));
// 填写arg结构参数
// 输出单个class函数
arg.w.fn =
qdisc_class_dump;
// 数据包指针
arg.skb = skb;
// 控制块指针
arg.cb = cb;
// 遍历结构walker参数
arg.w.stop =
0;
arg.w.skip =
cb->args[1];
arg.w.count = 0;
// 调用Qdisc类别操作结构的walk函数遍历该Qdisc所有类别
q->ops->cl_ops->walk(q,
&arg.w);
// 记录处理的类别数
cb->args[1] =
arg.w.count;
// 如果设置了停止标志, 退出循环
if (arg.w.stop)
break;
// 索引计数
t++;
}
read_unlock(&qdisc_tree_lock);
// 找过的Qdisc数, 有的Qdisc可能是跳过没处理的
cb->args[0] = t;
dev_put(dev);
return skb->len;
}
// 类别输出
static int qdisc_class_dump(struct Qdisc *q, unsigned long cl,
struct qdisc_walker *arg)
{
struct qdisc_dump_args *a = (struct
qdisc_dump_args *)arg;
// 调用TC class填充函数
return tc_fill_tclass(a->skb, q,
cl, NETLINK_CB(a->cb->skb).pid,
a->cb->nlh->nlmsg_seq,
NLM_F_MULTI, RTM_NEWTCLASS);
}
// 填充class参数用于netlink通信返回用户层
static int tc_fill_tclass(struct sk_buff *skb, struct Qdisc
*q,
unsigned long cl,
u32 pid, u32 seq, u16 flags, int event)
{
struct tcmsg *tcm;
struct nlmsghdr *nlh;
unsigned char *b =
skb->tail;
struct gnet_dump d;
// Qdisc的类别操作结构指针
struct Qdisc_class_ops *cl_ops =
q->ops->cl_ops;
// 在数据包缓冲区中定位填写的信息位置
nlh = NLMSG_NEW(skb, pid, seq, event,
sizeof(*tcm), flags);
// TC信息头位置
tcm = NLMSG_DATA(nlh);
// 填写TC信息参数
tcm->tcm_family = AF_UNSPEC;
tcm->tcm_ifindex =
q->dev->ifindex;
tcm->tcm_parent =
q->handle;
tcm->tcm_handle =
q->handle;
tcm->tcm_info = 0;
RTA_PUT(skb, TCA_KIND, IFNAMSIZ,
q->ops->id);
// 调用Qdisc类别参数的输出函数
if (cl_ops->dump
&& cl_ops->dump(q,
cl, skb, tcm) < 0)
goto rtattr_failure;
// 进行统计
if (gnet_stats_start_copy_compat(skb, TCA_STATS2,
TCA_STATS,
TCA_XSTATS,
q->stats_lock, &d) <
0)
goto rtattr_failure;
// 输出统计参数
if (cl_ops->dump_stats
&&
cl_ops->dump_stats(q, cl, &d)
< 0)
goto rtattr_failure;
if
(gnet_stats_finish_copy(&d) <
0)
goto rtattr_failure;
// 新添加的netlink信息长度
nlh->nlmsg_len =
skb->tail - b;
// 返回数据总长度
return skb->len;
nlmsg_failure:
rtattr_failure:
skb_trim(skb, b -
skb->data);
return -1;
}
7. filter操作
7.1 概述
tc filter命令是用来定义数据包进行分类的命令, 中间就要用到各种匹配条件, 其功能就象netfilter的match一样,
filter的处理和class的处理是紧密联系在一起的,用于完成对数据包的分类。
filter处理的基本api在net/sched/cls_api.c中定义,
而各种匹配方法在net/sched/cls_*.c中定义。
7.2 数据结构
// tc过滤协议结构, 这个结构在流控算法的分类函数中已经见过了
struct tcf_proto
{
// 链表中的下一项
struct tcf_proto *next;
// 根节点
void *root;
// 分类操作函数, 通常是tcf_proto_ops的classify函数, 就象Qdisc结构中的enqueue就是
// Qdisc_class_ops中的enqueue一样, 目的是向上层隐藏tcf_proto_ops结构
int (*classify)(struct
sk_buff*, struct tcf_proto*,
struct
tcf_result *);
// 协议
u32 protocol;
// 优先权
u32 prio;
// 类别ID
u32 classid;
// 流控节点
struct
Qdisc *q;
// 私有数据
void *data;
// filter操作结构
struct tcf_proto_ops *ops;
};
// filter操作结构, 实际就是定义匹配操作, 通常每个匹配操作都由一个静态tcf_proto_ops
// 结构定义, 作为一个内核模块, 初始化事登记系统的链表
struct tcf_proto_ops
{
// 链表中的下一项
struct
tcf_proto_ops *next;
// 名称
char kind[IFNAMSIZ];
// 分类操作
int (*classify)(struct
sk_buff*, struct tcf_proto*,
struct
tcf_result *);
// 初始化
int (*init)(struct
tcf_proto*);
// 释放
void (*destroy)(struct
tcf_proto*);
// 获取, 增加引用
unsigned
long (*get)(struct tcf_proto*,
u32 handle);
// 减少引用
void (*put)(struct
tcf_proto*, unsigned long);
// 参数修改
int (*change)(struct
tcf_proto*, unsigned long,
u32
handle, struct rtattr **,
unsigned
long *);
// 删除
int (*delete)(struct
tcf_proto*, unsigned long);
// 遍历
void (*walk)(struct
tcf_proto*, struct tcf_walker *arg);
// 输出
int (*dump)(struct
tcf_proto*, unsigned long,
struct
sk_buff *skb, struct tcmsg*);
// 模块指针
struct
module *owner;
};
// filter操作结果, 返回分类结果: 类别和类别ID
struct tcf_result
{
unsigned long class;
u32 classid;
};
7.3 初始化
static int __init tc_filter_init(void)
{
struct rtnetlink_link *link_p =
rtnetlink_links[PF_UNSPEC];
if (link_p) {
// 定义filter操作处理函数
// 关于filter的增加/删除/获取等操作
link_p[RTM_NEWTFILTER-RTM_BASE].doit
= tc_ctl_tfilter;
link_p[RTM_DELTFILTER-RTM_BASE].doit
= tc_ctl_tfilter;
link_p[RTM_GETTFILTER-RTM_BASE].doit
= tc_ctl_tfilter;
link_p[RTM_GETTFILTER-RTM_BASE].dumpit
= tc_dump_tfilter;
}
return 0;
}
7.4 filter控制
// 用于增加, 修改, 删除, 获取过滤结构
static int tc_ctl_tfilter(struct sk_buff *skb, struct nlmsghdr *n,
void *arg)
{
struct rtattr **tca;
struct tcmsg *t;
u32 protocol;
u32 prio;
u32 nprio;
u32 parent;
struct net_device *dev;
struct Qdisc *q;
struct tcf_proto **back, **chain;
// tc proto
struct tcf_proto *tp;
struct tcf_proto_ops *tp_ops;
struct Qdisc_class_ops *cops;
unsigned long cl;
// filter handle
unsigned long fh;
int err;
replay:
tca = arg;
t = NLMSG_DATA(n);
// TC信息的低16位是协议, 高16位是优先权
protocol =
TC_H_MIN(t->tcm_info);
prio =
TC_H_MAJ(t->tcm_info);
// 备份优先权参数
nprio = prio;
parent = t->tcm_parent;
cl = 0;
if (prio == 0) {
// 如果没指定优先权值, 在新建filter情况下是错误, 其他情况则构造一个缺省值
if
(n->nlmsg_type != RTM_NEWTFILTER ||
!(n->nlmsg_flags&NLM_F_CREATE))
return
-ENOENT;
prio =
TC_H_MAKE(0x80000000U,0U);
}
// 查找网卡设备
if ((dev =
__dev_get_by_index(t->tcm_ifindex)) == NULL)
return -ENODEV;
// 查找网卡所用的Qdisc
if (!parent) {
// 根节点的情况, 使用qdisc_sleeping
q =
dev->qdisc_sleeping;
parent =
q->handle;
// 非根节点的话根据handle查找
} else if ((q = qdisc_lookup(dev,
TC_H_MAJ(t->tcm_parent))) == NULL)
return -EINVAL;
// 如果该流控不支持分类操作, 返回失败
if ((cops =
q->ops->cl_ops) == NULL)
return -EINVAL;
// 低16位是子类别值
if (TC_H_MIN(parent)) {
// 获取类别结构, cl实际就是结构指针转的unsigned long值
cl =
cops->get(q, parent);
if (cl == 0)
return
-ENOENT;
}
// 获取过滤规则链表头地址, 因为是地址的地址, 所以这个值基本不应该是空的
chain = cops->tcf_chain(q,
cl);
err = -EINVAL;
if (chain == NULL)
goto errout;
// 遍历规则链表, 这个链表是有序表, 由小到大
for (back = chain; (tp=*back) != NULL; back =
&tp->next) {
// 如果某过滤规则的优先权值大于指定的prio
if (tp->prio
>= prio) {
if
(tp->prio == prio) {
// 如果优先权相同,
if
(!nprio || (tp->protocol != protocol
&& protocol))
goto
errout;
} else
// 否则优先权不同, 没有相同的优先权的节点, tp置为空
tp
= NULL;
break;
}
}
// 退出循环时, *back指向要链表中插入的位置后面那个的节点
if (tp == NULL) {
// tp为空, 当前规则中不存在指定优先权的节点
// 如果参数不全, 返回失败
if (tca[TCA_KIND-1] == NULL ||
!protocol)
goto
errout;
err = -ENOENT;
// 如果不是新建命令, 返回失败
if
(n->nlmsg_type != RTM_NEWTFILTER ||
!(n->nlmsg_flags&NLM_F_CREATE))
goto
errout;
// 分配新的tcf_proto结构节点
err = -ENOBUFS;
if ((tp = kmalloc(sizeof(*tp),
GFP_KERNEL)) == NULL)
goto
errout;
err = -EINVAL;
// 根据名称查找tp操作结构, 比如rsvp, u32, fw等
tp_ops =
tcf_proto_lookup_ops(tca[TCA_KIND-1]);
if (tp_ops == NULL) {
#ifdef CONFIG_KMOD
// 如果当前内核中没找到的话, 使用模块方式加载后重新查找
struct rtattr
*kind = tca[TCA_KIND-1];
char
name[IFNAMSIZ];
// 检查一下名称算法合法
if (kind !=
NULL &&
rtattr_strlcpy(name, kind, IFNAMSIZ) < IFNAMSIZ)
{
// 合法的话加载模块
rtnl_unlock();
request_module("cls_%s",
name);
rtnl_lock();
// 重新进行查找操作
tp_ops
= tcf_proto_lookup_ops(kind);
if
(tp_ops != NULL) {
// 找到的话还是返回错误, 不过是EAGAIN, 会重来一次
module_put(tp_ops->owner);
err
= -EAGAIN;
}
}
#endif
// 释放tcf_proto空间, 返回失败值
kfree(tp);
goto
errout;
}
// 查找成功的情况
// 结构空间清零
memset(tp, 0,
sizeof(*tp));
// 设置结构各参数
tp->ops =
tp_ops;
tp->protocol =
protocol;
tp->prio = nprio
? : tcf_auto_prio(*back);
tp->q = q;
// classify函数赋值
tp->classify =
tp_ops->classify;
tp->classid =
parent;
// 调用tp_ops的初始化函数初始化
if ((err =
tp_ops->init(tp)) != 0) {
module_put(tp_ops->owner);
kfree(tp);
goto
errout;
}
qdisc_lock_tree(dev);
// 将tp插入*back节点前面
tp->next =
*back;
// 更新*back, dummy header算法, 即使是第一次插入也是正确的
*back = tp;
qdisc_unlock_tree(dev);
}
// 找到了节点, 比较一下名称, 不同的话返回错误
else if (tca[TCA_KIND-1]
&& rtattr_strcmp(tca[TCA_KIND-1],
tp->ops->kind))
goto errout;
// 获取与t->tcm_handle对应的filter
fh =
tp->ops->get(tp,
t->tcm_handle);
if (fh == 0) {
// 获取filter失败
if
(n->nlmsg_type == RTM_DELTFILTER
&& t->tcm_handle ==
0) {
// 如果是删除命令, 而且TC信息的句柄为0, 则可认为删除操作是成功的
qdisc_lock_tree(dev);
// 将找到的tp从链表中断开
*back =
tp->next;
qdisc_unlock_tree(dev);
// 删除通告, 释放tp
tfilter_notify(skb,
n, tp, fh, RTM_DELTFILTER);
tcf_destroy(tp);
// err=0表示命令成功
err =
0;
goto
errout;
}
// 如果不是新建filter的话, 没找到filter就表示失败
err = -ENOENT;
if
(n->nlmsg_type != RTM_NEWTFILTER ||
!(n->nlmsg_flags&NLM_F_CREATE))
goto
errout;
} else {
// 找到filter, 根据命令类型进行操作
switch
(n->nlmsg_type) {
case
RTM_NEWTFILTER: // 新建filter, 如果定义了互斥标志, 返回错误, 因为filter已经存在了
err =
-EEXIST;
if
(n->nlmsg_flags&NLM_F_EXCL)
goto
errout;
break;
case RTM_DELTFILTER:
// 删除filter命令, 运行tcf_proto_ops的delete函数
err =
tp->ops->delete(tp, fh);
// 如果操作成功, 发送通告消息
if (err ==
0)
tfilter_notify(skb,
n, tp, fh, RTM_DELTFILTER);
goto
errout;
case RTM_GETTFILTER:
// 获取filter命令, 发送通告信息, 其中包含了filter的参数
err =
tfilter_notify(skb, n, tp, fh, RTM_NEWTFILTER);
goto
errout;
default:
err =
-EINVAL;
goto
errout;
}
}
// 新建,修改操作都通过tcf_proto_ops的change函数完成
err =
tp->ops->change(tp, cl,
t->tcm_handle, tca, &fh);
// 如果操作成功, 发送通告消息
if (err == 0)
tfilter_notify(skb, n, tp, fh,
RTM_NEWTFILTER);
errout:
// 减少cl引用
if (cl)
cops->put(q,
cl);
// 如果错误是EAGAIN, 重新操作
if (err == -EAGAIN)
goto replay;
return err;
}
// 根据名称查找tp_proto_ops
static struct tcf_proto_ops * tcf_proto_lookup_ops(struct rtattr
*kind)
{
struct tcf_proto_ops *t = NULL;
// 要指定tp_proto_ops的名称(字符串)
if (kind) {
read_lock(&cls_mod_lock);
// 遍历链表
for (t = tcf_proto_base; t; t =
t->next) {
// 比较名称是否相同
if
(rtattr_strcmp(kind, t->kind) == 0) {
// 找到的话增加模块引用计数, 如果该tp_proto_ops是模块的话, 中断循环返回
if
(!try_module_get(t->owner))
t
= NULL;
break;
}
}
read_unlock(&cls_mod_lock);
}
return t;
}
// filter通告
static int tfilter_notify(struct sk_buff *oskb, struct nlmsghdr
*n,
struct tcf_proto *tp, unsigned long fh, int event)
{
struct sk_buff *skb;
// 获取正在通信的用户进程的pid
u32 pid = oskb ? NETLINK_CB(oskb).pid : 0;
// 分配数据包用于发送
skb = alloc_skb(NLMSG_GOODSIZE,
GFP_KERNEL);
if (!skb)
return -ENOBUFS;
// 填充数据到skb中
if (tcf_fill_node(skb, tp, fh, pid,
n->nlmsg_seq, 0, event) <= 0) {
kfree_skb(skb);
return -EINVAL;
}
// 发送
return rtnetlink_send(skb, pid, RTNLGRP_TC,
n->nlmsg_flags&NLM_F_ECHO);
}
// 填充数据包
static int
tcf_fill_node(struct sk_buff *skb, struct tcf_proto *tp, unsigned
long fh,
u32 pid, u32 seq, u16 flags, int event)
{
struct tcmsg *tcm;
struct nlmsghdr *nlh;
unsigned char *b =
skb->tail;
// 填充pid, seq, event等参数, 到缓冲区, 同时将缓冲区剩余空间清零
nlh = NLMSG_NEW(skb, pid, seq, event,
sizeof(*tcm), flags);
// TC消息头
tcm = NLMSG_DATA(nlh);
// 填充TC消息
tcm->tcm_family = AF_UNSPEC;
tcm->tcm__pad1 = 0;
tcm->tcm__pad1 = 0;
tcm->tcm_ifindex =
tp->q->dev->ifindex;
tcm->tcm_parent =
tp->classid;
tcm->tcm_info =
TC_H_MAKE(tp->prio,
tp->protocol);
RTA_PUT(skb, TCA_KIND, IFNAMSIZ,
tp->ops->kind);
tcm->tcm_handle = fh;
// 如果不是删除事件
if (RTM_DELTFILTER != event) {
tcm->tcm_handle
= 0;
// 调用tp_ops的输出函数输出tp信息
if
(tp->ops->dump
&&
tp->ops->dump(tp, fh, skb, tcm)
< 0)
goto
rtattr_failure;
}
// 计算netlink消息长度
nlh->nlmsg_len =
skb->tail - b;
return skb->len;
nlmsg_failure:
rtattr_failure:
skb_trim(skb, b -
skb->data);
return -1;
}
7.5 filter输出
// 为方便输出定义的合并各数据的结构
struct tcf_dump_args
{
struct tcf_walker w;
struct sk_buff *skb;
struct netlink_callback *cb;
};
static int tc_dump_tfilter(struct sk_buff *skb, struct
netlink_callback *cb)
{
int t;
int s_t;
struct net_device *dev;
struct Qdisc *q;
struct tcf_proto *tp, **chain;
struct tcmsg *tcm = (struct
tcmsg*)NLMSG_DATA(cb->nlh);
unsigned long cl = 0;
struct Qdisc_class_ops *cops;
struct tcf_dump_args arg;
// 结构中的消息长度和结构大小不符, 返回的是数据包的当前数据长度, 也就是没加新数据
if
(cb->nlh->nlmsg_len <
NLMSG_LENGTH(sizeof(*tcm)))
return
skb->len;
// 查找网卡设备
if ((dev =
dev_get_by_index(tcm->tcm_ifindex)) == NULL)
return
skb->len;
read_lock(&qdisc_tree_lock);
// 查找相应的流控节点Qdisc
if (!tcm->tcm_parent)
// 根节点的情况
q =
dev->qdisc_sleeping;
else
// 非根节点的情况
q = qdisc_lookup(dev,
TC_H_MAJ(tcm->tcm_parent));
// 找不到Qdisc的话返回
if (!q)
goto out;
// 如果Qdisc是非分类的, 返回
if ((cops =
q->ops->cl_ops) == NULL)
goto errout;
// 类别值非0, 查找类别结构, 找不到的话也返回
if (TC_H_MIN(tcm->tcm_parent))
{
cl =
cops->get(q, tcm->tcm_parent);
if (cl == 0)
goto
errout;
}
// 过滤规则链表头地址
chain = cops->tcf_chain(q,
cl);
// 规则为空的话返回
if (chain == NULL)
goto errout;
// s_t是起始序号
s_t = cb->args[0];
// 遍历规则链表
for (tp=*chain, t=0; tp; tp =
tp->next, t++) {
// 序号小于起始序号的话, 跳过
if (t < s_t)
continue;
// 优先权不匹配的话, 跳过
if
(TC_H_MAJ(tcm->tcm_info)
&&
TC_H_MAJ(tcm->tcm_info) !=
tp->prio)
continue;
// 协议不匹配的话, 跳过
if
(TC_H_MIN(tcm->tcm_info)
&&
TC_H_MIN(tcm->tcm_info) !=
tp->protocol)
continue;
// 对于序号超过起始序号的那些节点, 清空args[1]起始的参数空间
if (t >
s_t)
memset(&cb->args[1],
0,
sizeof(cb->args)-sizeof(cb->args[0]));
if (cb->args[1]
== 0) {
// 高序号节点
// 填充tp信息, MULTI标志, NEWTFILTER(新建)类型
if
(tcf_fill_node(skb, tp, 0,
NETLINK_CB(cb->skb).pid,
cb->nlh->nlmsg_seq, NLM_F_MULTI,
RTM_NEWTFILTER) <= 0) {
break;
}
// 一个tp消息
cb->args[1]
= 1;
}
// 如果tp_ops的遍历操作为空, 跳过
if
(tp->ops->walk == NULL)
continue;
// 遍历输出各个节点参数
arg.w.fn = tcf_node_dump;
arg.skb = skb;
arg.cb = cb;
arg.w.stop = 0;
arg.w.skip =
cb->args[1]-1;
arg.w.count = 0;
tp->ops->walk(tp,
&arg.w);
// 数据的数量
cb->args[1] =
arg.w.count+1;
// 如果设置了stop标志, 中断
if (arg.w.stop)
break;
}
cb->args[0] = t;
errout:
if (cl)
cops->put(q,
cl);
out:
read_unlock(&qdisc_tree_lock);
dev_put(dev);
return skb->len;
}
// 填充tp节点
static int tcf_node_dump(struct tcf_proto *tp, unsigned long n,
struct tcf_walker *arg)
{
struct tcf_dump_args *a = (void*)arg;
// 填充tp信息到skb, MULTI标志, NEWTFILTER(新建)类型
return tcf_fill_node(a->skb, tp,
n, NETLINK_CB(a->cb->skb).pid,
a->cb->nlh->nlmsg_seq,
NLM_F_MULTI, RTM_NEWTFILTER);
}
...... 待续 ......