目前很多企业的内部局域网已经建立,很多还在局域网基础上建立了企业内部的MIS系统和邮件服务器等,甚至在系统中开展了视频点播等数据流量较大的业务。
  企业内部网络有足够的带宽可以使用。但是,一般在企业接入Internet的部分都是一个有限的流量。为了提高网络的使用质量,保证用户按照网络中业务设计的要求来使用整个网络的带宽,可以从流量控制服务器的角度分析、优化Linux系统,给企业服务带来便利和高效。
  流量控制的基本实现
  Linux操作系统中的流量控制器(TC)主要是通过在输出端口处建立一个队列来实现流量控制。Linux从2.1.105版内核开始支持流量控制,使用时需要重新编译内核。Linux流量控制的基本实现可简单地由图1来描述。从图1可以看出,内核是如何处理接收包、如何产生发送包,并送往网络的。
  图1 Linux流量控制的基本实现
  接收包进来后,由输入多路分配器(Input De-Multiplexing)进行判断选择:如果接收包的目的是本主机,那么将该包送给上层处理;否则需要进行转发,将接收包交到转发块(Forwarding Block)处理。转发块同时也接收本主机上层(TCP、UDP等)产生的包。转发块通过查看路由表,决定所处理包的下一跳。然后,对包进行排列以便将它们传送到输出接口(Output Interface)。Linux流量控制正是在排列时进行处理和实现的。
  图2 流量控制基本框架
  3.TC中过滤器(filter)的用法
  过滤器的使用中,内核支持需设置QoS support、QoS and/or fair queueing、Packet classifier API = y。用法为:
#tc filter [ add | del | change | get ] dev STRING
    [ pref PRIO ] [ protocol PROTO ]
    [ estimator INTERVAL TIME_CONSTANT ]
    [ root | classid CLASSID ] [ handle FILTERID ]
    [ [ FILTER_TYPE ] [ help | OPTIONS ] ]
#tc filter show [ dev STRING ] [ root | parent CLASSID ]
  其中
FILTER_TYPE := { rsvp | u32 | fw | route | etc. }
FILTERID := ... format depends on classifier, see there
OPTIONS := ... try tc filter add <desired FILTER_KIND> help
  TC U32过滤器(filter u32)的用法:
... u32 [ match SELECTOR ... ] [ link HTID ] [ classid CLASSID ]
   [ police POLICE_SPEC ] [ offset OFFSET_SPEC ]
   [ ht HTID ] [ hashkey HASHKEY_SPEC ]
   [ sample SAMPLE ]
或 u32 divisor DIVISOR
  其中
SELECTOR := SAMPLE SAMPLE ...
SAMPLE := { ip | ip6 | udp | tcp | icmp | u{32|16|8} } SAMPLE_ARGS
FILTERID := X:Y:Z
  TC FW过滤器(filter fw)的用法:
  ... fw [ classid CLASSID ] [ police POLICE_SPEC ]
  其中
POLICE_SPEC := ... look at TBF
CLASSID := X:Y
流量控制应用实例
  假设目前某企业面临的是一个复杂的网络环境。在该环境中,既包括了100Mb的局域网,也包括了微波或802.11的无线链路网络,其网络拓扑如图3所示。在这样的环境下,保证业务数据的流量,实现流量控制,需要对各局域网的传输及业务服务进行控制。
  图3 企业网络拓扑图
  如图2所示,Linux流量控制主要由三大部分来实现:
  ◆ 队列规则(Queue Discipline)
  ◆ 分类(Classes)
  ◆ 过滤器(Filters)
  因此,Linux流量控制主要分为建立队列、建立分类和建立过滤器三个方面。其基本实现步骤为:
  (1)针对网络物理设备(如以太网卡eth0)绑定一个队列;
  (2)在该队列上建立分类;
  (3)为每一分类建立一个基于路由的过滤器。
  流量控制的具体使用
  现在对流量控制(TC)的具体使用做个介绍。首先是TC的总用法。
  TC命令,内核支持需设置QoS support、QoS and/or fair queueing = y。用法为:
  #tc [ OPTIONS ] OBJECT { COMMAND | help }
  其中
  OBJECT := { qdisc | class | filter }
  OPTIONS := { -s[tatistics] | -d[etails] | -r[aw] }
  下面分别介绍TC中队列、分类和过滤器的用法。
  1.TC中队列(qdisc)的用法
  队列的使用中,内核支持需设置QoS support、QoS and/or fair queueing = y。用法为:
#tc qdisc [ add | del | replace | change | get ] dev STRING
    [ handle QHANDLE ] [ root | ingress | parent CLASSID ]
    [ estimator INTERVAL TIME_CONSTANT ]
    [ [ QDISC_KIND ] [ help | OPTIONS ] ]
#tc qdisc show [ dev STRING ] [ingress]
  其中
QDISC_KIND := { [p|b]fifo | tbf | prio | cbq | red | etc. }
OPTIONS := ... try tc qdisc add <desired QDISC_KIND> help
  CBQ队列(qdisc cbq)的使用中,内核支持需设置 QoS support、QoS and/or fair queueing、CBQ packet scheduler、Rate estimator = y,用法为:
... cbq bandwidth BPS avpkt BYTES [ mpu BYTES ]
        [ cell BYTES ] [ ewma LOG ]
  2.TC中分类(class)的用法
  分类的使用中,内核支持需设置QoS support、QoS and/or fair queueing、Packet classifier API = y。用法为:
#tc class [ add | del | change | get ] dev STRING
    [ classid CLASSID ] [ root | parent CLASSID ]
    [ [ QDISC_KIND ] [ help | OPTIONS ] ]
#tc class show [ dev STRING ] [ root | parent CLASSID ]
  其中
QDISC_KIND := { prio | cbq | etc. }
OPTIONS := ... try tc class add <desired QDISC_KIND> help
&nbsp;&nbsp;&nbsp;&nbsp;TC CBQ分类(class cbq)的用法为:
<ccid_nobr>
<table width="400" border="1" cellspacing="0" cellpadding="2"
bordercolorlight = "black" bordercolordark = "#FFFFFF" align="center">
<tr>
  <td bgcolor="e6e6e6" class="code" style="font-size:9pt">
  <pre><ccid_code> ... cbq bandwidth BPS rate BPS maxburst PKTS [ avpkt BYTES ]
    [ minburst PKTS ] [ bounded ] [ isolated ]
    [ allot BYTES ] [ mpu BYTES ] [ weight RATE ]
    [ prio NUMBER ] [ cell BYTES ] [ ewma LOG ]
    [ estimator INTERVAL TIME_CONSTANT ]
    [ split CLASSID ] [ defmap MASK/CHANGE ]
  1.建立队列
  一般情况下,针对一个网卡只需建立一个队列。
  将一个CBQ队列绑定到网络物理设备eth1上,其编号为1:0,实际带宽为10Mb,包的平均大小为1500字节,包间隔发送单元的大小为8字节,最小传输包大小为64字节。
  #tc qdisc add dev eth1 root handle 1:0 cbq bandwidth 10Mbit cell 8 avpkt 1500 mpu 64
  2.建立分类
  分类建立在队列之上。一般情况下,针对一个队列需建立一个根分类,然后在其上建立子分类。对于分类,按其分类的编号顺序起作用,编号小的优先。一旦符合某个分类匹配规则,通过该分类发送数据包,则其后的分类不再起作用。
  (1)创建根分类1:1,分配带宽为10Mb。
  #tc class add dev eth1 parent 1:0 classid 1:1 cbq bandwidth 10Mbit rate 10Mbit allot 1514 cell 8 weight 1Mbit maxburst 1 avpkt 1500 bounded
  该队列的最大可用带宽为10Mb,实际分配的带宽为10Mb,可接收冲突的发送最长包数目为1字节,最大传输单元(加MAC头)大小为1514字节,优先级别为8,包的平均大小为1500字节,包间隔发送单元的大小为8字节,相应于实际带宽的加权速率为10Mb。
  (2)创建分类1:2,其父分类为1:1,分配带宽为8Mb。
  #tc class add dev eth1 parent 1:1 classid 1:2 cbq bandwidth 10Mbit rate 1200Kbit allot 1514 cell 8 weight 120Kbit maxburst 1 avpkt 1500 bounded isolated
  该队列的最大可用带宽为10Mb,实际分配的带宽为 1.2Mb,可接收冲突的发送最长包数目为1字节,最大传输单元(加MAC头)大小为1514字节,优先级别为1,包的平均大小为1500字节,包间隔发送单元的大小为8字节,相应于实际带宽的加权速率为120Kb,独占带宽且不可借用未使用带宽。
  (3)创建分类1:10,其父分类为1:2,分配带宽为100Kb。
  #tc class add dev eth1 parent 1:2 classid 1:10 cbq bandwidth 10Mbit rate 100Kbit allot 1514 cell 8 weight 10Kbit maxburst 1 avpkt 1500
  该队列的最大可用带宽为10Mb,实际分配的带宽为 1.2Mb,可接收冲突的发送最长包数目为1字节,最大传输单元(加MAC头)大小为1514字节,优先级别为1,包的平均大小为1500字节,包间隔发送单元的大小为8字节,相应于实际带宽的加权速率为10Kb。
  (4)创建分类1:20,其父分类为1:2,分配带宽为1.1Mb。
  #tc class add dev eth1 parent 1:2 classid 1:20 cbq bandwidth 10Mbit rate 1100Kbit allot 1514 cell 8 weight 110Kbit maxburst 1 avpkt 1500
  详细显示指定设备(这里为eth1)的分类状况:
#tc -s class ls dev eth1
class cbq 1: root rate 10Mbit (bounded,isolated) prio no-transmit
Sent 17725304 bytes 32088 pkts (dropped 0, overlimits 0)
borrowed 0 overactions 0 avgidle 31 undertime 0
class cbq 1:1 parent 1: rate 10Mbit prio no-transmit
Sent 16627774 bytes 28884 pkts (dropped 0, overlimits 0)
borrowed 16163 overactions 0 avgidle 587 undertime 0
class cbq 1:2 parent 1:1 rate 1200Kbit prio no-transmit
Sent 628829 bytes 3130 pkts (dropped 0, overlimits 0)
borrowed 0 overactions 0 avgidle 4137 undertime 0
class cbq 1:10 parent 1:2 rate 100KMbit prio 1
Sent 0 bytes 0 pkts (dropped 0, overlimits 0)
borrowed 0 overactions 0 avgidle 159654 undertime 0
class cbq 1:20 parent 1:1 rate 1100Kbit prio no-transmit
Sent 5552879 bytes 8076 pkts (dropped 0, overlimits 0)
borrowed 3797 overactions 0 avgidle 159557 undertime 0
  这里主要显示通过不同分类发送的数据包、数据流量、丢弃的包数目、超过速率限制的包数目等。其中根分类(class cbq 1:0)的状况应与队列的状况类似。
  例如,分类class cbq 1:20发送了8076个数据包,数据流量为5552879个字节,丢弃的包数目为0,超过速率限制的包数目为0。
  (3)显示过滤器的状况
#tc -s filter ls dev eth1
filter parent 1: protocol ip pref 1 fw
filter parent 1: protocol ip pref 1 fw handle 0x4 classid 1:10
filter parent 1: protocol ip pref 100 fw
filter parent 1: protocol ip pref 100 fw handle 0x5 classid 1:20
  (4)显示现有ipchains的状况
#ip ipchains -L
Chain input (policy ACCEPT):
target prot opt   source  destination ports
-   all ------ anywhere anywhere  n/a
-   all ------ anywhere anywhere  n/a
-   udp ------ anywhere anywhere   any -> 7001:7004
Chain forward (policy ACCEPT):
Chain output (policy ACCEPT):
target prot opt   source  destination ports
-    all ------ anywhere anywhere   n/a
-    all ------ anywhere anywhere   n/a
-    udp ------ anywhere anywhere   7001:7004 -> any
-    udp ------ anywhere anywhere   7001:7004 -> any
  该队列的最大可用带宽为10Mb,实际分配的带宽为 1.1Mb,可接收冲突的发送最长包数目为1字节,最大传输单元(加MAC头)大小为1514字节,优先级别为1,包的平均大小为1500字节,包间隔发送单元的大小为8字节,相应于实际带宽的加权速率为110Kb。
  3.建立过滤器
  过滤器主要服务于分类。一般只需针对根分类提供一个过滤器,然后为每个子分类提供一个ipchains映射。
  (1)应用FW分类器到分类1:10,父分类编号为1:0,过滤协议为IP,标志号(handle)为4,过滤器为基于ipchains。
  #tc filter add dev eth1 protocol ip parent 1:0 prio 1 handle 4 fw classid 1:10
  (2)应用FW分类器到分类1:20,父分类编号为1:0,过滤协议为IP,过滤器为基于ipchains。
  #tc filter add dev eth1 protocol ip parent 1:0 prio 100 handle 5 fw classid 1:20
  4.建立ipchains映射
  该路由与前面所建立的路由映射一一对应。
  (1)由本机发出的电话数据包通过分类1:10转发(分类1:10的速率100Kb)
  #ipchains -A output -p udp -i tun0 -s 202.168.200.188/32 7001:7004 -t 0x01 0x10 -m 4
  (2)由局域网发出的数据包通过分类1:20转发(分类1:20的速率1.1Mb/s)
  #ipchains -A output -i tun0 -s 192.16.188.0/24 -m 5
  注意,一般对于流量控制器所直接连接的网段,建议使用IP主机地址流量控制限制,不要使用子网流量控制限制。如一定需要对直连子网使用子网流量控制限制,则在建立该子网的ipchains映射前,需将原先由系统建立的ipchains删除,才可完成相应步骤。
  5.监视
  主要包括对现有队列、分类、过滤器和路由状况的监视。
  (1)显示队列的状况
  简单显示指定设备(这里为eth1)的队列状况:
#tc qdisc ls dev eth1
qdisc cbq 1: rate 10Mbit (bounded,isolated) prio no-transmit
  详细显示指定设备(这里为eth1)的队列状况:
#tc -s qdisc ls dev eth1
qdisc cbq 1: rate 10Mbit (bounded,isolated) prio no-transmit
Sent 7646731 bytes 13232 pkts (dropped 0, overlimits 0)
borrowed 0 overactions 0 avgidle 31 undertime 0
  这里主要显示通过该队列发送了13232个数据包,数据流量为7646731个字节,丢弃的包数目为0,超过速率限制的包数目为0。
  (2)显示分类的状况
  简单显示指定设备(这里为eth1)的分类状况:
  系统采用Linux 2.2.14版内核来支持QoS。首先需要重新编译内核。运行make config,进行如下设置:
EXPERIMENTAL _OPTIONS = y
Class Based Queueing (CBQ) = y
QoS and/or fair queueing = y
CBQ packet scheduler = y
Rate estimator= y
Packet classifier API = y
  编译生成新内核:
#make dep
#make clean
#make bzImage
  Linux操作系统中,流量控制器(TC)在输出端口处建立一个队列进行流量控制,控制的方式基于目的IP地址、目的子网的网络号及端口号,或者基于源IP地址、源子网的网络号及端口号。
  流量控制器TC的基本功能模块为队列、分类和过滤器。Linux内核中支持的队列有Class Based Queue、Token Bucket Flow、CSZ、First In First Out、Priority、TEQL、SFQ、ATM、RED。这里讨论的队列与分类都是基于CBQ(Class Based Queue),过滤器则是基于U32和FW。
  配置和使用流量控制器TC,除了建立队列、分类、过滤器和路由外,还需要对现有的队列、分类、过滤器和ipchains进行监视。
  基于如图3所示的网络环境,现对中心流量控制服务器进行简单描述,如图4所示。
  图4 中心服务器接口图
  eth1的配置
  假设远端无线网卡(eth1)的IP地址为172.16.32.196,在其上建立一个CBQ队列,控制所有从无线网卡发出的数据包。假设包的平均大小为1000字节,包间隔发送单元的大小为8字节,可接收冲突的发送最长包数目为20字节。
  这里采用源地址及源端口号进行控制。假如有三种类型的流量需要控制:
  ◆ 由本主机发出的业务数据包,源端口为7001:7004,流量带宽控制在100Kb;
  ◆由本地局域网发出的数据包,IP地址为192.168.1.26,流量带宽控制在1.1Mb;
  ◆发往子网1,子网号为192.168.1.0,子网掩码为255.255.255.0,流量带宽控制在1Mb。
  6. 维护
  主要包括对队列、分类、过滤器和路由的增添、修改和删除。
  增添动作一般依照队列→分类→过滤器→ipchains的顺序进行;修改动作没有什么特殊要求;删除则依照ipchains→过滤器→分类→队列的顺序进行。
  这里采用的方法是全部删除,然后重新设置。删除命令如下:
#tc qdisc del root dev eth1
#ipchains -F
  eth0的配置
  eth0的配置与eth1的配置方法一样。配置如下(说明略):
#tc qdisc del root dev eth0
#tc qdisc add dev eth0 root handle 1: cbq bandwidth
10Mbit cell 8 avpkt 1500 mpu 64
#tc class add dev eth0 parent 1:0 classid 1:1 cbq bandwidth
10Mbit rate 10Mbit allot 1514 cell 8 weight 1Mbit prio 8
maxburst 1 avpkt 1500 bounded
#tc class add dev eth0 parent 1:1 classid 1:10 cbq bandwidth
10Mbit rate 1.4Mbit allot 1514 cell 8 weight 140Kbit prio 8
maxburst 1 avpkt 1500 bounded isolated
#tc filter add dev eth0 protocol ip parent 1:0 prio 100 handle 3 fw classid 1:10
#ipchains -A output -i eth0 -d 0.0.0.0/0 -m 3
  通过对企业流量控制服务器的分析,可以对Linux系统进行简单改造,以实现对一些特殊业务的流量预分配和业务保证。