2016年7月8日 星期五

TC流量控制 ingress

From http://dp2u.com/2014/tc-control.html

Introduction
Linux操作系統中的流量控制器TC(Traffic Control)用於Linux內核的流量控制,它利用隊列規定建立處理數據包的隊列,並定義隊列中的數據包被發送的方式,從而實現對流量的控制。
TC模塊實現流量控制功能使用的隊列規定分為兩類,一類是無類隊列規定,另一類是分類隊列規定。無類隊列規定相對簡單,而分類隊列規定則引出了分類和過濾器等概念,使其流量控制功能增強。
無類隊列規定是對進入網絡設備(網卡)的數據流不加區分統一對待的隊列規定。使用無類隊列規定形成的隊列能夠接受數據包以及重新編排、延遲或丟棄數據包。這類隊列規定形成的隊列可以對整個網絡設備( 網卡)的流量進行整形,但不能細分各種情況。常用的無類隊列規定主要有pfifo _fast (先進現出)、TBF(令牌桶過濾器)、SFQ(隨機公平隊列)、ID (前向隨機丟包)等等。這類隊列規定使用的流量整形手段主要是排序、限速和丟包。
分類隊列規定是對進入網絡設備的數據包根據不同的需求以分類的方式區分對待的隊列規定。數據包進入一個分類的隊列後,它就需要被送到某一個類中,也就是說需要對數據包做分類處理。對數據包進行分類的工具是過濾器,過濾器會返回一個決定,隊列規定就根據這個決定把數據包送入相應的類進行排隊。每個子類都可以再次使用它們的過濾器進行進一步的分類。直到不需要進一步分類時,數據包才進入該類包含的隊列排隊。除了能夠包含其它隊列規定之外,絕大多數分類的隊列規定還能夠對流量進行整形。這對於需要同時進行調度(如使用SFQ)和流量控制的場合非常有用。

Principle

一 内核处理流程示意图
  1. 用户级程序
  2. ^
  3. |
  4. +---------------+-------------------------------------+
  5. | Y |
  6. | -------> IP 协议栈 |
  7. | | | |
  8. | | Y |
  9. | | Y |
  10. | ^ | |
  11. | | / ------> 转发--------> |
  12. | ^ / | |
  13. | |/ Y |
  14. | | | |
  15. | 多路分配器 Y /-队列规定 1-\ |
  16. | | Egress /--队列规定 2--\ |
  17. In--->->Ingress 分类器-----队列规定 3------|-->Out
  18. | 队列规定 \__队列规定 4__/ |
  19. | \-队列规定 N_/ |
  20. | |
  21. +-----------------------------------------------------+
接收包從輸入接口(Input Interface)進來後,經過流量限制(Ingress Policing)丟棄不符合規定的數據包,由輸入多路分配器(Input De-Multiplexing)進行判斷選擇:如果接收包的目的是本主機,那麼將該包送給上層處理;否則需要進行轉發,將接收包交到轉發塊(Forwarding Block)處理。轉發塊同時也接收本主機上層(TCP、UDP等)產生的包。轉發塊通過查看路由表,決定所處理包的下一跳。然後對包按照定義的隊列規定進行排列以便將它們傳送到輸出接口(Output Interface)。Linux流量控制主要是在輸出接口排列時進行處理和實現的。

##二TC規則
###1.流量控制方式
流量控制包括以下幾種方式:
SHAPING(限制) 當流量被限制,它的傳輸速率就被控制在某個值以下。限制值可以大大小於有效帶寬,這樣可以平滑突發數據流量,使網絡更為穩定。shaping(限制)只適用於向外的流量。
SCHEDULING(調度) 通過調度數據包的傳輸,可以在帶寬範圍內,按照優先級分配帶寬。SCHEDULING(調度)也只適於向外的流量。
POLICING(策略) SHAPING用於處理向外的流量,而POLICIING(策略)用於處理接收到的數據。
DROPPING(丟棄) 如果流量超過某個設定的帶寬,就丟棄數據包,不管是向內還是向外。
###2.流量控制處理對象
流量的處理由三種對象控制,它們是:qdisc(排隊規則)、class(類別)和filter(過濾器)。
Qdisc(排隊規則)是queueing discipline的簡寫,它是理解流量控制(traffic control)的基礎。無論何時,內核如果需要通過某個網絡接口發送數據包,它都需要按照為這個接口配置的qdisc(排隊規則)把數據包加入隊列。然後,內核會盡可能多地從qdisc裡面取出數據包,把它們交給網絡適配器驅動模塊。
TC的大多數隊列規定(qdisc)都是用於輸出方向的,輸入方向只有一個隊列規定,即Ingress qdisc。因此一般主要是限製網卡發送的數據包,而不是限製網卡接收的數據包。可以通過定義的若干個隊列規定來改變數據包的發送次序,實現傳輸速率的控制。
而Ingress qdisc本身的功能很有限,只能通過Filter過濾出需要處理的數據包Drop掉。通過Ingress qdisc可以把輸入方向的數據包重定向到一個虛擬設​​備ifb,然後在ifb的輸出方向可以配置多種qdisc,就可以達到對輸入方向的流量做隊列調度的目的。
Class(類別)組成一個樹,每個類都只有一個父類,而一個類可以有多個子類。某些QDisc(例如:CBQ和HTB)允許在運行時動態添加類,而其它的QDisc(例如:PRIO)不允許動態建立類。允許動態添加類的QDisc可以有零個或者多個子類,由它們為數據包排隊。
此外,每個類都有一個葉子QDisc,默認情況下,這個葉子QDisc使用pfifo的方式排隊,我們也可以使用其它類型的QDisc代替這個默認的QDisc。當一個數據包進入一個分類QDisc,它會被歸入某個子類。
###3.命名規則
所有的QDisc、類和過濾器都有ID。ID可以手工設置,也可以有內核自動分配。
ID由一個主序列號和一個從序列號組成,兩個數字用一個冒號分開。
QDISC,一個QDisc會被分配一個主序列號,叫做句柄(handle),然後把從序列號作為類的命名空間。句柄採用象10:一樣的表達方式。習慣上,需要為有子類的QDisc顯式地分配一個句柄。
類(CLASS),在同一個QDisc裡面的類分享這個QDisc的主序列號,但是每個類都有自己的從序列號,叫做類識別符(classid)。類識別符只與父QDisc有關,和父類無關。類的命名習慣和QDisc的相同。
過濾器(FILTER),過濾器的ID有三部分,只有在對過濾器進行散列組織才會用到。詳情請參考tc-filters手冊頁。

HOWTO

##一限制下載
先看個簡單的限制下載速度的例子
  1. #創建一個HTB的根,默認類為1:20
  2. tc qdisc add dev eth0 root handle 1 : htb default 20
  3. #創建一個HTB的類,流量的限制就是在這裡限制的,並設置突發。
  4. tc class add dev eth0 parent 1 : classid 1 : 1 htb rate 800kbit ceil 800kbit burst 80k
  5. #創建一個過濾規則把要限制流量的數據過濾出來,並發給上面的類來限制速度。
  6. tc filter add dev eth0 parent 1 : prio 1 protocol ip u32 match ip src 1.1 . 1.1 / 32 match ip sport 80 0xffff flowid 1 : 1
通過這樣簡單的配置,就實現了對web服務器下載流量的限制,限制每個客戶端流量為800Kbits。
參數說明:
  1. rate rate allocated to this class ( class can still borrow ) 是一個類保證得到的帶寬值,如果有不只一個類,請保證所有子類總和是小於或等於父類
  2. burst max bytes burst which can be accumulated during idle period 突發流量
  3. ceil definite upper class rate ( no borrows ) 是一個類最大能得到帶寬值
  4. prio priority of leaf ; lower are served first 是優先權的設置,數值越大,優先權越小,如果是分配剩餘帶寬,就是數值小的會最優先取得剩餘的空閒的帶寬權
從上面這個例子中,看到TC實現流量控制的基本步驟------建立隊列,建立分類,建立過濾器。
通過以下命令可以查看隊列,分類,過濾器的狀態:
  1. [ root@test ~] # tc qdisc ls dev eth0 ##列出當前的隊列規定
  2. qdisc htb 1 : r2q 10 default 20 direct_packets_stat 611
  3. [ root@test ~] # tc class ls dev eth0 ##列出當前的分類
  4. class htb 1 : 1 root prio 0 rate 800000bit ceil 800000bit burst 80Kb cburst 2000b
  5. [ root@test ~] # tc filter ls dev eth0 ##列出當前的過濾器
  6. filter parent 1 : protocol ip pref 1 u32
  7. filter parent 1 : protocol ip pref 1 u32 fh 800 : ht divisor 1
  8. filter parent 1 : protocol ip pref 1 u32 fh 800 :: 800 order 2048 key ht 800 bkt 0 flowid 1 : 1
  9. match 0a795f40 / ffffffff at 12
  10. match 00500000 / ffff0000 at 20
  11. [ root@test ~] # tc -s -d qdisc show dev eth0 ##顯示當前隊列的詳細狀態數據
  12. qdisc htb 1 : r2q 10 default 20 direct_packets_stat 5356 ver 3.17
  13. Sent 415693 bytes 5356 pkt ( dropped 0 , overlimits 0 requeues 0 )
  14. rate 0bit 0pps backlog 0b 0p requeues 0
  15. [ root@test ~] # tc -s -d filter show dev eth0 ##顯示當前過濾器的詳細狀態數據
  16. filter parent 1 : protocol ip pref 1 u32
  17. filter parent 1 : protocol ip pref 1 u32 fh 800 : ht divisor 1
  18. filter parent 1 : protocol ip pref 1 u32 fh 800 :: 800 order 2048 key ht 800 bkt 0 flowid 1 : 1 ( rule hit 6626 success 0 )
  19. match 0a795f40 / ffffffff at 12 ( success 6626 )
  20. match 00500000 / ffff0000 at 20 ( success 0 )
通過以下命令可以刪除添加的隊列規定:
  1. [ root@test ~] # tc qdisc del dev eth0 root
TC其他的命令如替換,改變等,可以查看命令的help或man
  1. [ root@test ~] # tc qdisc help
  2. Usage : tc qdisc [ add | del | replace | change | get ] dev STRING
  3. [ handle QHANDLE ] [ root | ingress | parent CLASSID ]
  4. [ estimator INTERVAL TIME_CONSTANT ]
  5. [ [ QDISC_KIND ] [ help | OPTIONS ] ]
  6. tc qdisc show [ dev STRING ] [ ingress ]
再詳細說一下U32過濾器,U32可以根據數據包中各個字段進行匹配,非常強大。
從我們之前的例子中來說明一下U32過濾器的用法,命令如下:
  1. u32 match ip src 1.1 . 1.1 / 32 match ip sport 80 0xffff
這條匹配的意思是源地址為1.1.1.1,源端口為80的數據包,我們的例子是對一台web服務器限下載速度,對於下載來說,數據包都是從web服務器發出的。
再看一下查看過濾器狀態時的顯示:
match 01010101/ffffffff at 12 0a795f40轉換為10進制就是上面的IP,ffffffff是掩碼,at 12代表從數據包的ip頭的12字節開始匹配,從IP頭結構可知第13字節開始是IP源地址match 00500000/ffff0000 at 20 00500000實際是源端口和目的端口組合,一共佔4個字節,前2個字節是源端口,50轉換成10進制就是80端口,ffff0000相當於掩碼,源端口嚴格限定80,目的端口不限
同理,根據源/目的地址:
  1. match ip src 0.0 . 0.0 / 0  
  2. match ip dst 1.2 . 3.0 / 24  
根據源/目的端口可以這樣表示:
  1. match ip sport 80 0xffff  
  2. match ip dport 80 0xffff
根據IP協議:
  1. match ip protocol ( udp tcp icmp gre ipsec )
  2. 比如icmp 協議是1 match ip protocol 1 0xff
##二限制上傳
因為上傳對於服務器來說,是入站流量,因此只能通過Ingress qdisc來處理,但Ingress qdisc本身功能很弱,所以我們這裡使用虛擬設備ifb來處理入站數據
首先需要加載內核模塊,啟用虛擬網卡設備ifb0
  1. [ root@test ~] # modprobe ifb
  2. [ root@test ~] # ip link set dev ifb0 up
然後對入站流量做設置,鏡像到ifb0上
  1. [ root@test ~] # tc qdisc add dev eth0 ingress
  2. [ root@test ~] # tc filter add dev eth0 parent ffff: protocol ip u32 match u32 0 0 flowid 1:1 action mirred egress redirect dev ifb0
然後我們就可以對ifb0接口的out方向流量做各種限制了
  1. [ root@test ~] # tc qdisc add dev ifb0 root handle 1: htb default 1
  2. [ root@test ~] # tc class add dev ifb0 parent 1: classid 1:1 htb rate 800kbit ceil 800kbit burst 80k
  3. [ root@test ~] # tc filter add dev ifb0 parent 1: prio 1 protocol ip u32 match ip dst 1.1.1.1/32 match ip dport 80 0xffff flowid 1:1
上傳一個文件測試限制是否生效
  1. [ root@test ~] # tc -s -d filter show dev ifb0
  2. filter parent 1 : protocol ip pref 1 u32
  3. filter parent 1 : protocol ip pref 1 u32 fh 800 : ht divisor 1
  4. filter parent 1 : protocol ip pref 1 u32 fh 800 :: 800 order 2048 key ht 800 bkt 0 flowid 1 : 1 ( rule hit 98978 success 7718 )
  5. match 01010101 / ffffffff at 16 ( success 94889 )
  6. match 00000050 / 0000ffff at 20 ( success 7718 )
  7. [ root@test ~] # tc -s -d class show dev ifb0
  8. class htb 1 : 1 root prio 0 quantum 10000 rate 800000bit ceil 800000bit burst 80Kb / 8 mpu 0b overhead 0b cburst 2000b / 8 mpu 0b overhead 0b level 0
  9. Sent 20401293 bytes 99941 pkt ( dropped 1068 , overlimits 0 requeues 0 )
  10. rate 22928bit 39pps backlog 0b 0p requeues 0
  11. lended : 99941 borrowed : 0 giants : 0
  12. tokens : 818160 ctokens : 18960
  13. [ root@test ~] # tc -s -d qdisc show dev ifb0
  14. qdisc htb 1 : r2q 10 default 1 direct_packets_stat 116 ver 3.17
  15. Sent 20523886 bytes 101601 pkt ( dropped 1068 , overlimits 22466 requeues 0 )
  16. rate 0bit 0pps backlog 0b 0p requeues 0
從上面的數據中可以看出,有1068個包被Drop掉了。
##三一點思考
TC適合用在什麼場景下?
TC適合用於限制出站流量的場景,比如提供軟件下載的服務器。因為TC對出站流量有很多種隊列規定可用,而且在服務器上控制發送數據的速率,真正實現了對帶寬佔用的減少。
TC並不太適合用於限制入站流量的場景。TC入站流量限制的功能很弱,而在作為接收端的服務器上做限制,超過速率限制後,服務器端是將數據包drop掉了,導致發送端重傳,一定程度上造成了資源的浪費,用戶體驗的降低。

How to use simple speedtest in RaspberryPi CLI

  pi@ChunchaiRPI2:/tmp $  wget -O speedtest-cli https://raw.githubusercontent.com/sivel/speedtest-cli/master/speedtest.py --2023-06-26 10:4...