linux中流的定义,Linux内核中流量控制(16)

本文档的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);

}

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

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值