TC流量控制

概述

Linux操作系統中的流量控制器TC(Traffic Control)用於Linux內核的流量控制,它利用隊列規定(qdisc)建立處理數據包的隊列,並定義隊列中的數據包被髮送的方式, 從而實現對流量的控制。TC模塊實現流量控制功能使用的隊列規定分爲兩類,一類是無類隊列規定(classless qdisc), 另一類是分類隊列規定(classful qdisc)。 無類隊列規定相對簡單,而分類隊列規定則引出了分類和過濾器等概念,使其流量控制功能增強。

  • classless qdisc對進入網絡設備的數據流不加區分的統一對待,
    • 無類別qdisc中,數據包被接收、重新編排、延遲或丟棄,實現對整個設備發出的流量進行整形處理,但是所有數據包統一處理,不能細分各種情況。
    • 常用classless qdisc有pfifo_fast(先進先出,默認)、TBF(令牌桶過濾器)、SFQ(隨機公平隊列)、ID(向前隨機丟包)等。
  • classful qdisc對進入設備的數據包根據不同的需求以分類的方式區分對待的qdisc。
    • 數據包進入一個分類的qdisc中,就需要根據需要被送到某一個類中,在對應的類中進行分類處理,這個分類的根據就是過濾器,過濾器根據設定的規則以及包的屬性,覺得該數據包被送往哪個制定的隊列,沒有匹配到過濾器的包就被送到默認的類中進行處理。
    • 每一個子類也可以包含自己的過濾器,被分配到該子類的數據包,會在子類的過濾器中進一步匹配,直到被匹配到制定的類中,或者被分配到默認的類中。
    • 類除了可以包含其他隊列規定之外,絕大多數分類的隊列還可以對流量進行整形,這對於同時需要進行調度和流量控制的場合非常有用。

基本原理

TC的基本原理是將準備發給接口發送的報重新進行排序、整理和設置優先級,使得數據包在發送的最後關頭,按照設置的限制重新組合。
TC對於包的處理方式有四種:
1. SHAPING:控制,當流量被shaped,它的傳輸速率就被控制了,但是shaping不僅僅是降低可用的帶寬,也被用於平抑流量中的突發流量,可以實現更好的網絡體驗。shaping只發送在流量出口的地方。
2. SCHEDULING:調度,通過調度數據包的傳輸,可以提高對需要它的通信的交互性,同時還可以保證帶寬的傳輸。這種重新排序也被稱爲按照優先級排序,並且只發生在出口。
3. POLICING:監管,與shaping處理數據報的傳遞對應的,監管屬於處理數據報到達時的處理方法,因此POLICING發生在入口處。
4. DROPPING:丟棄,超出指定帶寬的數據報可能會立即被拋棄,這在入口和出口都會發生。
一般來說,TC只對網絡設備發出的流量進行限制,不對接收的流量進行限制,所以在設計規則時,要注意數據的流向,比如上行速率的限制都是在外網接口進行,下行速率的限制都是在內網接口進行

TC通過三種結構qdisc(隊列)、class(分類)和filter(過濾器)來組合成各種定製的數據包分發結構,這種結構類似於一個樹,qdisc是樹的節點,是每一個結構節點的載體,規定和限制了該節點可以承擔怎樣的功能,派生出怎樣的子節點;class可以看做樹上節點的值,它具體定義了該節點對於流量的帶寬、優先級、延遲和burst的規定;filter就是節點的閘門,管理者數據包是否應該流入該節點。整個流量在TC樹中的流動,就是這樣從上到下,從小的minor_id到大的minor_id的類依次輪詢,也有例外的就是,如果一個類下面的子類設置有不同的優先級,則流量會優先滿足優先級高的子類(prio_id小的)。
首先,進入的是根qdisc,即根class,然後在根class下的filter之前過濾,決定該包進入哪一個子類,如果都沒有匹配到,則進入default的子類,如果匹配到一個子類,則進入該子類進行進一步的filter,直到匹配到沒有子類的子類,或被分配到默認的子類中。

隊列QDISC

無類別qdisc

無類別的qdisc,包括以下三種

  • [p|b]fifo:使用最簡單的filter,純粹的先入先出,只通過一個參數limit限制隊列的長度,pfifo中limit以數據包的個數爲單位,bfifo中limit以字節數爲單位。
  • pfifo_fast:當內核打開“高級路由(Advanced Router)”編譯選項時,系統的標準qdisc,它的隊列包括三個波段,每個波段都是用先入先出規則,但是三個波段優先級不同,從高到低依次是band0, - - band1和band2。系統優先處理高優先級的波段中的數據報,這些數據報是按照服務類型被分配到三個波段裏的。
  • red:Random Early Detection,隨機早期探測,使用該qdisc時,當單開的佔用接近規定的帶寬時,系統會隨機的丟棄一些數據包,非常適用於高帶寬的應用。
  • sfq:Stochastic Fairness Queueing,隨機公平隊列,它會按照會話ID(session ID)爲流量進行排序,然後循環按時每個會話的數據包。
  • tbf:Token Bucket Filter,令牌桶過濾器,適用於降低帶寬到一個精確配置的速率,很適用於大帶寬。
    如果沒有有類別的qdisc,那麼無類別qdisc只能附屬於一個設備的根,用法如下:
tc qdisc add dev DEV root QDISC QDISC-PARAMETERS

使用如下命令刪除該設備的qdisc:tc qdisc del dev DEV root。在沒有配置qdisc時,pfifo_fast就是默認的qdisc。

有類別qdisc

有類別qdisc包括:
- CBQ:Class Based Queueing,基於類別排隊,實現了一個具有豐富共享層次結構的類結構,它既有shaping限制帶寬的能力,也具有帶寬優先級管理的能力,帶寬限制是通過計算機連接的空閒時間完成的,而空閒時間的計算標準就是數據報離隊事件的頻率和下層數據鏈路層的帶寬。
- HTB:Hierarchy Token Bucket,層次結構令牌桶,實現了一個具有豐富鏈接共享的層次結構,重點是與現有的實踐相一致。使用HTB可以很容易的保證每個類別的帶寬,雖然它也允許特定的類可以突破帶寬上限,佔用別的類的帶寬。它包含基於TBF的限制元素來限制帶寬,可以爲類設置優先級。
- PRIO:不能限制帶寬,因爲不同類別的數據包是順序離隊的,使用PRIO qdisc可以很容易的對流量進行優先級管理,只有高優先級的數據包全部發送完之後,低優先級的數據包纔會繼續被髮送。爲了方便管理,需要使用iptables或ipchain(iptables的前身)處理數據包的TOS。

分類Class

分類class和qdisc總是成對出現,class爲qdisc指定具體的整形、限流和優先級規則,由於不同的qdisc功能差別較大,其所屬class的規則格式也差別很大,在使用時,需要查看具體的文檔說明。注意,無類別qdisc沒有class。

過濾器Filter

filter用於指定數據包被分配給哪一個類,除了默認的類之外,每一個class都應該有對應的filter來爲他匹配數據包。

命名規則

  • 一個qdisc會被分配一個主序列號,被稱爲handle(句柄),然後把從序列號作爲類的命名空間。句柄採用類似10:這樣的表達方式。一般一個根qdisc被分配一個handle,類似handle_id:,該handle_id就是其下所有子類的major_id,這個id是表示該子類所屬的標誌,不能更改。每個子類分配的qdisc,使用該子類的minor_id構建類似minor_id的格式來產生自己的handle。
tc qdisc add dev eth0 root handle 2: htb default 100
  • 一個class的命名格式:major_id:minor_id,其中major_id必須和父類的major_id以及父qdisc的handle_id一致,所以在一個設備下,所有類的major_id都是一致的,這也就意味着該設備下的所有類的minor_id不能重複,不然內核會報出該配置重複的錯誤RTNETLINK answers: File exists
tc class add dev eth0 parent 2:0 classid 2:1 htb rate 5000kbit burst 15k
tc class add dev eth0 parent 2:1 classid 2:20 htb rate 1000kbit ceil 1000kbit burst 15k
  • 一個filter沒有具體的命名,它針對每一個具體的分類建立過濾規則,命令格式如下
"tc filter add dev eth0 parent 2:0 protocol ip handle 0x0101 fw classid 2:20",

TC命令規則

TC的工作原理是在輸出端口建立一個隊列進行流量控制,控制的對象是從該端口發出數據包,控制的方式是基於路由,即根據數據包的IP地址、目的子網地址、MARK值等進行的流量控制。TC功能主要通過qdisc、class和filter三種功能模塊實現,在添加TC命令時,也是通過這三種功能模塊和路由(Route、iptables)的組合來實現具體的限速、整形功能。
以下例子中的隊列和分類基於常用的HTB實現,過濾器基於iptables功能,實現一個簡單的單層TC命令樹,實現對內網特定設備的下載速度的限制,控制設備爲內網路由器,LAN側網卡eth0,IP:192.168.2.1,內網設備1,mac:11:11:11:11:11:11,ip:192.168.2.102, 限速1MB/s,內網設備2,mac:22:22:22:22:22:22,ip:192.168.2.103,限速500KB/s,其餘設備不限速。
基本步驟如下:

  1. 針對一個網絡設備,如eth0,綁定一個根的htb隊列
  2. 在該隊列上建立分類
  3. 爲每一個分類建立一個無類別sfq qdisc,以及一個基於iptables的過濾器
  4. 最後,位於過濾器配置,設置特定的iptables規則

綁定根隊列和根分類

爲控制內網設備的下載速度,需要控制路由器發給該設備的流量,即路由器eth0接口發往內網設備的流量,所以在eth0上建立tc規則如下:

tc qdisc add dev eth0 root handle 10: htb default 100

tc建立htb根隊列,開頭和添加其他qdisc一樣tc qdisc add;隨後指定添加隊列的設備dev eht0,並指定這是根隊列,沒有parent,所以後面跟root;再指定handle 10:,這是qdisc命名的普遍格式,注意隨後添加的分類major_id要和handle冒號前的值一致;隨後,指出此次添加的qdisc類型爲htb,到此爲止,之前的格式都是通用的,所有的qdisc的添加都會採用類似的格式,除了根隊列需要添加root,而子隊列需要制定parent;最後,每種qdisc可能需要不同的參數,有的是必填的,有的是可選的,htb必填的參數只有default,其後跟着流量默認走向的子類的minor_id,該子類可以不去手動添加,系統會自動按照默認的配置生成一個隱藏的子類,但是通過tc show的命令並不能看到該子類,所以一般建議是添加該默認子類,以便對流量進行監控和統計。

然後,爲該根隊列綁定一個根分類,如下:

tc class add dev eth0 parent 10:0 classid 10:1 htb rate 1000Mbit burst 15k

開頭和所有添加class的命令一致tc class add dev eth0,隨後指定父類,由於是根class,所以父類爲parent 10:0,其實表示的就是根qidsc,10:0的表示和10:的表示是一樣的;指定分類IDclassid 10:1,指定分類所屬qdisc類型htb,隨後是針對該分類的屬性配置,rate 1000Mbit表示分配給該類的帶寬,注意這裏是bit而不是B,換算成網速需要除以8,而且如果對於整個網絡設備的整個帶寬不做限制的話,這裏的rate其實是遠遠超過實際帶寬的,如果需要對整個帶寬做限制,在這裏就需要對rate做出具體的限制;burst 15k表示在指定帶寬基礎上,允許的burst脈衝,即1000Mbit帶寬基礎上,允許超出該值得最大波動,即帶寬波動到(1000000+15)kbit都是合理且允許的。

建立所需分類

根據要求,eth0下面有兩個設備需要限速,其餘設備不進行限速,所以需要建立兩個class爲這兩個設備限速進行使用,再建立一個默認的class作爲默認的流量通道。

tc class add dev eth0 parent 10:1 classid 10:2 htb rate 8000kbit ceil 8000kbit burst 15k
tc class add dev eth0 parent 10:1 classid 10:3 htb rate 4000kbit ceil 4000kbit burst 15k
tc class add dev eth0 parent 10:1 classid 10:100 htb rate 100kbit ceil 1000Mbit burst 15k

第一條爲設備1的tc規則,classid 10:2,限速rate 8000kbit即1MB/s,ceil 8000kbit是htb的一個特殊屬性,在htb中存在一種借用機制,即同一個父類下面的子類,允許向其他子類借用其閒置的帶寬資源,但是這個借用的大小有限制,借用的帶寬+自身rate的帶寬<=ceil,這裏對設備1進行限速,所以講rate和ceil都設爲8000kbit(其實默認情況下,ceil會被自動設置爲rate一樣的值)。
第二條爲設備2的tc規則,classid 10:3,格式與1一樣,只是限速值爲4000kbit
第三條爲默認子類的tc規則,這裏rate 100kbit是一個很小的值,但是ceil 1000Mbit是整個設置的帶寬的大小,這表示其餘設備雖然被分配了很小的帶寬,但是允許借用設備下所有的限制帶寬流量,這也是爲了在網絡擁堵的情況下,防止陌生設備過度佔用帶寬。

爲每個分類建立sfq qdisc和filter

tc爲每個分類默認的定義一個pfifo_fast的qdisc,用於處理該分類所接收的流量,但是如果遇到某一設備下的數據量很大,大量佔用帶寬的情況,其餘走同一分類的流量較小的設備就無法及時得到處理和響應。所以,這裏不採用默認的pfifo_fast隊列,而是採用sfq隊列,該隊列會將數據包打亂髮送,以避免上述情況發生,使得每個設備的數據都有較爲公平的機會被處理。
由於這裏針對具體設備進行過濾,並且知道設備的mac地址,所以tc爲每個分類建立的filter可以基於iptables,通過iptables爲制定mac和流向的數據打上mark,在tc中通過mark來匹配響應控制的流量。

tc qdisc add dev eth0 parent 10:2 handle 2: sfq perturb 10
tc filter add dev eth0 parent 10:0 protocol ip handle 0x0002 fw classid 10:2

tc qdisc add dev eth0 parent 10:3 handle 3: sfq perturb 10
tc filter add dev eth0 parent 10:0 protocol ip handle 0x0003 fw classid 10:3

tc qdisc add dev eth0 parent 10:100 handle 100: sfq perturb 10

爲一個分類建立qdisc時,需要注意parent 10:2爲該分類的classidhandle爲該分類classidminor_id,指定qdisc類型爲sfqsfq必須的參數爲perturb 10,表示每10s產生一次隨機算法的擾動,即每10s對隨機排隊算法進行一次小的調整,防止固定的隨機算法會同樣造成默寫設備的流量被一直阻塞或被排在較低的優先級。該值不能太小,一般建議在60,否則會導致一些數據包的丟失或重新錄入。
建立filter時,開頭一致爲tc filter add dev eth0,隨後是指定parent 10:0,因爲這裏的filter是掛在根class下面的,所以父節點爲根class的classid,隨後指定過濾器起作用的協議爲ipprotocol iphandle 0x0002表示過濾的目標爲mark = 0x0002的數據包,fw classid 10:2表示匹配到目標數據包後將其調度到classid10:2的分類中。
最後一條爲默認分類建立sfq,不再需要添加filter

設置特定iptables規則

由於需要限制eth0->設備1以及eth0->設備2的流量,所以將該方向上的數據加上mark,注意set-mark的動作只能在mangle表中進行。

iptables -t mangle -A FOR_WARD -J tc_ctl
iptables -t mangle -A tc_ctl -d 192.168.2.102 -o eth0 -j MARK --set-mark 0x0002
iptables -t mangle -A tc_ctl -d 192.168.2.103 -o eth0 -j MARK --set-mark 0x0003

監控

在tc運行過程中,通過tc show命令來查看tc命令是否正確的添加和生效,通過iptables -t mangle -nvL命令查看添加的iptables命令。

  • 查看qdisc,可以顯示qdisc的handle、默認分類等信息,加-s查看詳細信息,包括髮送的數據包總數,便於查看設置的限速規則是否正確生效。
#tc qdisc dev eth0 show
qdisc htb 10: root refcnt 2 r2q 10 default 100 direct_packets_stat 0
qdisc sfq 2: parent 10:2 limit 127p quantum 1514b divisor 1024 perturb 10sec 
qdisc sfq 3: parent 10:3 limit 127p quantum 1514b divisor 1024 perturb 10sec
#tc -s qdisc dev eth0 show
qdisc htb 10: root refcnt 2 r2q 10 default 20 direct_packets_stat 0
 Sent 62218 bytes 229 pkt (dropped 0, overlimits 2 requeues 0) 
 backlog 0b 0p requeues 0 

qdisc sfq 2: parent 2:10 limit 127p quantum 1514b divisor 1024 perturb 10sec 
 Sent 0 bytes 0 pkt (dropped 0, overlimits 0 requeues 0) 

qdisc sfq 3: parent 2:20 limit 127p quantum 1514b divisor 1024 perturb 10sec 
 Sent 62218 bytes 222 pkt (dropped 0, overlimits 0 requeues 0) 
 backlog 0b 0p requeues 0
  • 查看class,加-s查看詳細信息
# tc class show dev eth0
class htb 10:2 parent 10:1 leaf 2: prio 0 rate 8000Kbit ceil 8000Kbit burst 15Kb cburst 1600b 
class htb 10:1 root rate 1000Mbit ceil 1000Mbit burst 15125b cburst 1375b 
class htb 10:3 parent 10:1 leaf 3: prio 0 rate 4000kbit ceil 4000kbit burst 15Kb cburst 1375b
class htb 10:100 parent 10:100 leaf 100: prio 0 rate 100kbit ceil 1000Mbit burst 15Kb cburst 1375b
# tc -s class show dev eth0
class htb 10:2 parent 10:1 leaf 2: prio 0 rate 8000Kbit ceil 8000Kbit burst 15Kb cburst 1600b 
 Sent 0 bytes 0 pkt (dropped 0, overlimits 0 requeues 0) 
 rate 0bit 0pps backlog 0b 0p requeues 0 
 lended: 0 borrowed: 0 giants: 0
 tokens: 480000 ctokens: 50000

class htb 10:3 parent 10:1 leaf 3: prio 0 rate 4000Kbit ceil 4000Kbit burst 15Kb cburst 1600b 
 Sent 0 bytes 0 pkt (dropped 0, overlimits 0 requeues 0) 
 rate 0bit 0pps backlog 0b 0p requeues 0 
 lended: 0 borrowed: 0 giants: 0
 tokens: 480000 ctokens: 50000

class htb 10:1 root rate 1000Mbit ceil 1000Mbit burst 15125b cburst 1375b 
 Sent 92361 bytes 608 pkt (dropped 0, overlimits 0 requeues 0) 
 rate 264bit 0pps backlog 0b 0p requeues 0 
 lended: 7 borrowed: 0 giants: 0
 tokens: 1906 ctokens: 187

class htb 10:100 parent 10:1 leaf 100: prio 0 rate 100000bit ceil 1000Mbit burst 15Kb cburst 1375b 
 Sent 92361 bytes 608 pkt (dropped 0, overlimits 0 requeues 0) 
 rate 264bit 0pps backlog 0b 0p requeues 0 
 lended: 594 borrowed: 7 giants: 0
 tokens: 19120000 ctokens: 187
  • 查看過濾器
# tc filter show dev eth0
filter parent 10: protocol ip pref 49152 fw 
filter parent 10: protocol ip pref 49152 fw handle 0x2 classid 10:2
filter parent 10: protocol ip pref 49152 fw handle 0x3 classid 10:3
  • 查看iptables規則
# iptables -t mangle -nvL
...

Chain FORWARD (policy ACCEPT 42 packets, 2608 bytes)
 pkts bytes target     prot opt in     out     source               destination         
   42  2608 tc_ctl     all  --  *      *       0.0.0.0/0            0.0.0.0/0           

...

Chain ip_ctl (1 references)
 pkts bytes target     prot opt in     out     source               destination         
    0     0 MARK       all  --  --    eth0   0.0.0.0/0            192.168.2.102/24            MARK set 0x2
    0     0 MARK       all  --  --    eth0   0.0.0.0/0            192.168.2.103/24            MARK set 0x3

維護

tc運行過程中,需要根據實際運行情況動態調整tc規則,tc提供了changadddelete等命令可以動態的調整tc規則,但是要注意,有些qdisc可以動態調整,但是有些是不能動態調整的。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章