tc分析-转载

 

分类: LINUX

2014-01-06 10:07:26

原文地址:tc分析 (转) 作者:ccjsj1

 

一.
包括
tc qdisc add dev eth0 root sfq的内核流程
sch_sfq.c文件分析
tbf的原理
sch_tbf.c的分析
文件: tc桌面.rar
大小: 23KB
下载: 下载
 
 
二.
其实我前段时间才真正开始研究网络。以前看过情景分析,把socket层基本搞清楚。往下是inet socket层,也不是很难。再往下就是传输层,TCP/UDP,虽然看过TCP/IP详解的一,二卷本,但仍一头雾水,看不懂(原因估计是太庞大,又盘根错节)。决定再从下往上。

先看了net/core/*.c,许多都看不同,但总算有了一些认识。然后看drivers/net/ne.c,loopback.c驱动,知道了驱动大概情况。

再往上就是qos了,也就是net/sched/*.c.从最简单的看起,sch_generic.c(其中有noop_disc,noqueue_qdisc,pfifo_fast_qdisc),sch_fifo.c。这些基本内容之后就看sch_sfq.c,sch_tbf.c,这两个稍微复杂一点,但是和别的东西独立(只是牵涉到af_netlink,sch_api.c),因此也比较容易理解.

再往后看了sch_prio.c,这是一个可分类的规则,很简单,但也很重要。因为能够分类(能够挂接其他队列规则),因此又涉及到分类器。于是从前天到昨天花了两天时间集中研究了u32分类器(cls_u32.c),这个分类器功能很强大,因此结构异常复杂。然后附带研究了一下fw分类器,这个分类器原理很简单。

分析的时候我同时进行试验,先在在virtual pc上,然后实战。我们学校的网关是我装的,redhat 7.2(2.4.7-10smp).以前照葫芦画瓢建了两个简单tc规则.
tc qidsc add dev eth0 root sfq perturb 10
tc qidsc add dev eth1 root sfq perturb 10
eth0接外网,eth1接内网,iptables做nat和过滤包。

现在稍微懂了一些qos后,修改了一下规则
#!/bin/bash -x

for i in 0 1
do
/sbin/tc qdisc del dev eth$i root
/sbin/tc qdisc add dev eth$i handle 1: root prio bands 3 priomap 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2
/sbin/tc qdisc add dev eth$i parent 1:1 sfq perturb 10
/sbin/tc qdisc add dev eth$i parent 1:2 sfq perturb 10
/sbin/tc qdisc add dev eth$i parent 1:3 sfq perturb 10
if [ $i -eq 0 ];then port="dport";addr="16";else port="sport";addr="12";fi
/sbin/tc filter add dev eth$i parent 1: pref 10 protocol ip u32 match ip protocol  6 0xff match ip $port 80 0xffff flowid 1:1
/sbin/tc filter add dev eth$i parent 1: pref 10 protocol ip u32 match u32 0xaabbccdd 0xffffffe0 at $addr flowid 1:2
done

这样可以对校内的流量进行象样的qos了,保证http流量优先,其次是访问我们自己的公网服务器的流量,最后是其他流量,这些流量都是通过u32分类器进行分类.当然也可以使用fw分类器来完成这些任务。试验的时候有些命令不会用就看Linux的高级路由和流量控制HOWTO.pdf,然后在对照iproute2中tc的源代码,可以起到事半功倍的效果,而不是以前的抓瞎了(tc比较难用,有没有正规的手册)。

接下来我准备继续把其他的几个算法看完,包括cls_route,cls_tcindex,sch_red,sch_gred,sch_teql,sch_dsmask,sch_cbq(这个最麻烦了),sch_ingress.等等。

把这些看完就了后,往上到邻居子系统层(arp),再到ip层(包括路由子系统,netfilter子系统,icmp,igmp,ipip,等等),最后再猛攻UDP/TCP.

路还很长,但我有信心走下去。
 
三.
sch_prio队列规则的分析
cls_u332分类器的分析
cls_fw分类器的分析
文件: tc桌面2.rar
大小: 41KB
下载: 下载
 
四.
这方面的资料比较匮乏,我只有Linux的高级路由和流量控制HOWTO.pdf,其实源代码才是最宝贵的第一手资料(包括tc的)

修正发现的错误:
1.令牌桶过滤器的原理中
“如果令牌不够,数据需要等待一定的时间才被发送,这个时间是由latency参数控制。”这句话有误,这个时间不是由latency参数控制的,而是根据还需要多少令牌来计算延迟的时间,然后启动看门狗定时器。

2.u32分类器的数据结构的组织理解有误,修改过的文件在附件中

u32分类器比较复杂(非常容易写错),而fw却很简单(非常容易写),建议大家用fw.
文件: tc filter add u32.rar
大小: 11KB
下载: 下载
 
 
五.
关于sch_ingress.c



使用ingress的内核处理流程
tc qdisc add dev eth0 handle ffff: ingress
tc filter add dev eth0 parent ffff: pref 10 protocol ip u32

1.init_module->nf_register_hook
注册ingress的钩子到
static struct nf_hook_ops ing_ops =
{
        { NULL, NULL},
        ing_hook,
        PF_INET,// 2
        NF_IP_PRE_ROUTING,//注册到PRE_ROUTING
        NF_IP_PRI_FILTER + 1
};

2.网卡接收到一个ip数据包后调用ip_input.c中的ip_rcv
NF_HOOK(PF_INET, NF_IP_PRE_ROUTING, skb, dev, NULL,ip_rcv_finish)
NF_HOOK定义如下
#define NF_HOOK(pf, hook, skb, indev, outdev, okfn)                        \
(list_empty(&nf_hooks[(pf)][(hook)])                                        \
? (okfn)(skb)                                                                \
: nf_hook_slow((pf), (hook), (skb), (indev), (outdev), (okfn)))
因此将调用nf_hook_slow,在core/netfilter.c中
nf_hook_slow->nf_iterate-> hook-> ing_hook(ingress的钩子函数)-> ingress_enqueue(入队)-> tc_classify(分类)

3.根据分类结果决定继续处理该包,还是丢弃(例如被管制器管制
 
 
六.
sch_teql.c

teql原理

1.在Linux的高级路由和流量控制HOWTO.pdf有一些说明,可以参照

2.teql实现了一个简单的多网卡负载均衡.简单,是因为它不考虑实际的硬件情况,只是在关联的实际设备之间轮转转发包,即使是一个千兆网卡和MODEM被关联在一起.可以称做发包数负载均衡吧.

3.insmod sch_teql.o时, init_module会注册一个名为teql0的虚网络接口和一个名为teql0的队列规程,他们被集成在struct teql_master结构中,彼此分不开.
可以使用一下命令绑定实设备到队列规程teql0
tc qdisc add dev eth0 root teql0
tc qdisc add dev eth1 root teql0
然后用命令ip link set dev teql0 up激活虚设备teql0
这样一个teql设备就配置完成了
接下来可以再配置另一个teql设备
insmod –o sch_teql1 sch_teql.o
我这是RED HAT 7.2,由于已经加载了sch_teql.o模块,只能重命名加载了.
新版本内核已经实现一次insmod就可指定若干个teql
tc qdisc add dev eth2 root teql1
tc qdisc add dev eth3 root teql1
ip link set dev teql1 up

4.当有数据包到达teql0时先进入自己的队列,缺省为pfifo_fast,然后内核就调用teql_master_xmit来在从设备中循环轮转选择一个设备eth*发包.如果teql*是该实设备的根队列规程,就直接调用eth*的hard_start_xmit函数发包.如果不是就选择下一个设备,只至成功或失败.而配置在该设备上的teql*规程,它的行为就类似于pfifo规程.这是给那些直接通过该设备发送的包准备的规程.
文件: teql.rar
大小: 11KB
下载: 下载
 
 
七.
sch_red.c random early detection
随机早期探测

参考 中的early.pdf

说明
1.qlen 为当前队列长度
2.qave 平均队列长度,它的计算公式是qave=qave*(1-W)+qlen*W,W为权重,W一般选得很小 ,这可以使突发包对qave不会产生太大波动
3.qmin,qmax 当qmin<=qave<=qmax时,开始随机标记/丢包,标记/丢包的概率为
max_P * (qave- qmin)/(qmax-qmin),,随着qave增长,概率逼近max_P
标记包是希望客户端知道目前网络已经开始拥挤,放慢发包速度吧
如果qave>=qmax,标记/丢弃所有的包
4.如果网络空闲了一段时间,就应该相应地减少qave的大小,qave=qave*(1-W)^m,m为空闲时间段长度
文件: sch_red.rar
大小: 5KB
下载: 下载
 
 
八.
路由分类器cls_route.c
路由分类器的原理很简单,就是在路由的时候由路由规则对数据包打标记.路由分类器就用这个标记来对数据包分类.
路由规则语法如下
ip route add Host/Network via Gateway dev Device realm RealmNumber

ip route add 192.168.10.0/24 via 192.168.10.1 dev eth1 realm 10
至于如何打标记,我现在还不了解
在设置了路由规则以后,就可以使用路由分类器分类数据了
tc filter add dev eth0 protocol ip prio 100 route to 10 classid :2
tc filter add dev eth0 protocol ip prio 100 route from 10 classid :1
路由分类器参考的参数有fromdev/from,to,fromdev和from互斥,只能使用其中一个,由他们在分类器的二层hash表中索引和查找.

资源预留协议分类器cls_rsvp.h
资源预留协议分类器的原理也很简单,就是用ip协议(也支持ipv6,此处不涉及)的目的地址,可能的目的端口,源地址,可能的源端口来索引和查找一个二层hash表.
"可能的"意思是不一定使用,由用户决定.也就是它是基于session(会话)的.
tc filter add dev eth0 pref 10 protocol ip rsvp ipproto tcp session 10.1.1.1/80 classid :1
这条规则的意思就是,到10.1.1.1:80的数据流分类到:1的队列
还可以加入sender,指定发送者,或者不指定端口,而指定一个GPI,也就是在offset处的值&mask==key方式更加灵活地指定一个匹配.
文件: route_rsvp.rar
大小: 22KB
下载: 下载
 
 
九.
sch_dsmark.c和cls_tcindex.c

diffserv是一个体系,需要一个有若干个路由和一定规模的网络来协作实现,就单个主机来说用处不大.在这个网络中,大家都遵守同样的diffserv协定,例如哪个dsfield的优先级高,哪个低,以及怎样处理等.

这里引入了域的概念,就是遵守某一diffserv协定的网络组成一个ds域.剩下的就是非ds域.一台域中的主机要发的包,可以在自己的出口队列分类,或在接入的路由上分类.
1.在本机分类,出口绑定dsmark队列规程和u32等分类器
2.在路由分类,路由入口绑定ingress队列规程和u32等分类器
注:上面的分类器一般不用tcindex分类器,因为是从非ds域到ds域的转换,而tcindex实用于使用已有ds field来分类流(见3)和不同ds域之间的转换,不同域之间的转换一般发生在入口上,例如上面的2,如果数据是从另外一个ds域来的话.
这样所有的流就被区分开了.
3.然后路由器就可以在自己的出口绑定dsmark队列规程(和一些内部队列,例如cbq)和tcindex分类器,让tcidnex分类来对不同级别的流(只根据ds field)进行整形和qos.

上面都是我的理解,化了我很长时间,不对之处,请大家指正.大家参考lartc上的说明.
下面的例子也是摘之lartc
tc qdisc add dev eth0 handle 1:0 root dsmark indices 64 set_tc_index#绑定dsmark队列规程
tc filter add dev eth0 parent 1:0 protocol ip prio 1 tcindex mask 0xfc shift 2#建立tcindex分类器
tc qdisc add dev eth0 parent 1:0 handle 2:0 cbq bandwidth 10Mbit cell 8 avpkt 1000 mpu 64 # EF traffic class内部队列
tc class add dev eth0 parent 2:0 classid 2:1 cbq bandwidth 10Mbit rate 1500Kbit avpkt 1000 prio 1 bounded isolated allot 1514 weight 1 maxburst 10 # Packet fifo qdisc for EF traffic子类
tc qdisc add dev eth0 parent 2:1 pfifo limit 5 #子类的队列规程
tc filter add dev eth0 parent 1:0 protocol ip prio 1 handle 0x2e tcindex classid 2:1 pass_on #例子中是parent 2:0,我认为是parent 1:0,把EF流分类到2:1子类,不懂EF流是怎么回事,半桶水^_^
文件: dsmark_tcindex.rar
大小: 13KB
下载: 下载
 
 
十.
sch_gred.c  Generic Random Early Detection

这个算法是在red的基础上扩展引入virtual queue的概念形成的.
在gred中最多可以引入16个vq,其中至少有一个缺省vq.
每个vq都基本按red算法来操作(有区别),但所有的这些vq都公用一个实际的队列
sch->q.

gred算法有四种模式,由参数eqp和grio控制(不知道是什么的缩写)
(注意是否开始随机丢包由qave+q->qaveqth_min控制)
eqp   grio
0     0  每个vq一个独立red (qave=0),但共享一个实队列
0     1  每个vq和比它优先级高的vq构成一个部分相关red,保证优先级高的vq优先发包
qave+q->qave的值是按照相关的每个vq自己计算的ave的总和
1     0  每个vq一个部分相关red,和sch->backlog相关 (qave=0)
q->qave的值是把sch->backlog按本vq的方式计算ave的值
1     1  每个vq一个全部相关red (qave=0)
q->qave的值是把sch->backlog按本vq的方式计算ave的累计总和

我认为比较有用的是(0,0)(有点类似于sfq)和(0,1)(有点类似于prio)
gred实际上和red一样都比较难配置,主要应用于路由器上
因为它是采用skb->tc_index来选择vq的,可以和dsmark规程和tcindex分类器协作
文件: gred.rar
大小: 5KB
下载: 下载
 
 
十一.
estimator.c和police.c

--------------------------------------------------------------
estimator用于估计包的速度,包括bps(每秒字节数)和pps(每秒包数).
estimator的调用独立于qos架构,每个estimator一个内核定时器,可以每1/4s,1/2s,1s,2s,4s,8s被调用一次,定时计算

它可以应用在队列规程上和分类器上.
1.在队列规程上,它把计算结果放在qdisc->stats中,到目前为止我还没有看到谁在使用这个结果,不过这可以让用户监视和评估该规程上的流量
2.在分类器上,在分类器的每个策略中都有一个tcf_police结构,估计结果就放入该结构的stats成员中.在策略被命中后,将调用tcf_police,如果使用了估计器,tc_police就根据估计结果决定通过该策略的数据流量是否超限,是的话就执行规定的动作,ok,drop,reclassify,unspec.

Usage: ... estimator INTERVAL TIME-CONST
interval为定时间隔,time-const的计算方法是w=interval/time-const,w为加权比,例est 1s 8s,则定时间隔为1s,w=1/8=0.125

------------------------------------------------------------------
police用于分类器上,前面已经提到,策略被命中就会调用tcf_police,对通过该策略数据的进行管制

除了使用可选的estimator的结果进行管制以外,police主要使用tbf(令牌桶过滤器)对流进行管制操作.tbf的理论在前面已经叙述过了.tbf使用常规桶和峰值桶来控制流量.对于超限的包执行规定的动作.

Usage: ... police rate BPS burst BYTES[/BYTES] [ mtu BYTES[/BYTES] ]  
                [ peakrate BPS ] [ avrate BPS ]                       
                [ ACTION ]                                            
Where: ACTION := reclassify | drop | continue
avrate用于estimator,判断流量是否超限
lartc上有一个"防护SYN洪水攻击例子"用到police
iptables -A PREROUTING -i $INDEV -t mangle -p tcp --syn -j MARK --set-mark 1
$TC qdisc add dev $INDEV handle ffff: ingress
$TC filter add dev $INDEV parent ffff: protocol ip prio 50 handle 1 fw
police rate 1kbit burst 40 mtu 9k drop flowid :1
 
posted @ 2022-02-21 11:26  rebeca8  阅读(279)  评论(0编辑  收藏  举报