流量控制--4.軟體和工具
阿新 • • 發佈:2020-11-19
## 軟體和工具
### 5.1 核心要求
許多發行版都為核心提供了模組化或整體式的流量控制(QOS)。自定義的核心可能不會支援這些特性。
對核心編譯不瞭解或經驗不多的使用者建議閱讀[Kernel-HOWTO](http://linuxdocs.org/HOWTOs/Kernel-HOWTO.html)。對於熟練的核心編譯者在瞭解流量控制之後就可以確定需要開啟如下哪些選項。
例1.核心的編譯選項
```
#
# QoS and/or fair queueing
#
CONFIG_NET_SCHED=y
CONFIG_NET_SCH_CBQ=m
CONFIG_NET_SCH_HTB=m
CONFIG_NET_SCH_CSZ=m
CONFIG_NET_SCH_PRIO=m
CONFIG_NET_SCH_RED=m
CONFIG_NET_SCH_SFQ=m
CONFIG_NET_SCH_TEQL=m
CONFIG_NET_SCH_TBF=m
CONFIG_NET_SCH_GRED=m
CONFIG_NET_SCH_DSMARK=m
CONFIG_NET_SCH_INGRESS=m
CONFIG_NET_QOS=y
CONFIG_NET_ESTIMATOR=y
CONFIG_NET_CLS=y
CONFIG_NET_CLS_TCINDEX=m
CONFIG_NET_CLS_ROUTE4=m
CONFIG_NET_CLS_ROUTE=y
CONFIG_NET_CLS_FW=m
CONFIG_NET_CLS_U32=m
CONFIG_NET_CLS_RSVP=m
CONFIG_NET_CLS_RSVP6=m
CONFIG_NET_CLS_POLICE=y
```
使用如上選項編譯的核心可以為本文討論的所有場景提供模組化支援。使用者在使用一個給定的特性前可能需要執行**modprobe \*`module`\***。
### 5.2 iproute2工具(tc)
iproute2是一個命令列套件,可以用於管理一臺機器上與IP網路配置有關的核心結構。如果要檢視這些工具技術文件,可以參閱[iproute2 文件](http://linux-ip.net/gl/ip-cref/),如果要了解更具探討性的內容,請參閱[linux-ip.net](http://linuxdocs.org/HOWTOs/Kernel-HOWTO.html)上的文件。在iproute2工具包中,二進位制的**tc**是唯一用於流量控制的工具。本文件將忽略其他工具。
由於tc需要與核心互動來建立,刪除和修改流量控制結構,因此在編譯tc時需要支援所有期望的`qdisc`。實際上,在iproute2上游包中還不支援HTB qdisc。更多資訊參見[Section 7.1, “HTB, Hierarchical Token Bucket”](https://tldp.org/en/Traffic-Control-HOWTO/ar01s07.html#qc-htb) 。
tc工具會執行支援流量控制所需要的所有核心結構配置。由於它的用法多種多樣,其命令語法也是晦澀難懂的。該工具將三個Linux流量控制組件(qdisc、class或filter)中的一個作為其第一個必選引數。
例2.tc命令的用法
```shell
[root@leander]# tc
Usage: tc [ OPTIONS ] OBJECT { COMMAND | help }
where OBJECT := { qdisc | class | filter }
OPTIONS := { -s[tatistics] | -d[etails] | -r[aw] }
```
每個物件都可以接收其他不同的選項,本文後續將會進行完整的描述。下例展示了tc命令列語法的多種用法。更多用法可以參見 [LARTC HOWTO](http://lartc.org/howto/)。如果要更好地瞭解tc,可以參閱核心和iproute2原始碼。
例3.tc qdisc
```
[root@leander]# tc qdisc add \ <1>
> dev eth0 \ <2>
> root \ <3>
> handle 1:0 \ <4>
> htb <5>
```
1. 新增一個佇列規則,動作也可以為`del`
2. 指定附加新佇列規則的裝置
3. 表示"ergess",此處必須使用`root`。另外一個qdisc的功能是受限的,ingress qdisc可以附加到相同的裝置上
4. [handle](https://tldp.org/en/Traffic-Control-HOWTO/ar01s04.html#c-handle)是一個使用*`major`*:*`minor`*格式指定的使用者自定義號。當使用佇列規則控制代碼時,次號必須為0。qdisc控制代碼可以簡寫為"1:"(等同於"1:0"),如果沒有指定,則次號預設為0
5. 附加的佇列規則,上例為HTB。佇列規則指定的引數將會跟在後面。上例中沒有指定任何引數
上述展示了使用tc工具將一個佇列規則新增到一個裝置的用法。下面是使用tc給現有的父類新增類的用法:
例4.tc 類
```
[root@leander]# tc class add \ <1>
> dev eth0 \ <2>
> parent 1:1 \ <3>
> classid 1:6 \ <4>
> htb \ <5>
> rate 256kbit \ <6>
> ceil 512kbit <7>
```
1. 新增一個類,動作也可以為`del`
2. 指定附加的新類的裝置
3. 指定附加的新類的父控制代碼
4. 表示類的唯一控制代碼(主:次)。次號必須為非0值
5. 這兩個[classful qdiscs](https://tldp.org/en/Traffic-Control-HOWTO/ar01s07.html)都要求任何子類使用與父類相同型別的類。此時一個HTB qdisc將包含一個HTB類
6. 7. 這是一個類指定的引數,更多參見[Section 7.1, “HTB, Hierarchical Token Bucket”](https://tldp.org/en/Traffic-Control-HOWTO/ar01s07.html#qc-htb)
例5. tc過濾器
```
[root@leander]# tc filter add \ <1>
> dev eth0 \ <2>
> parent 1:0 \ <3>
> protocol ip \ <4>
> prio 5 \ <5>
> u32 \ <6>
> match ip port 22 0xffff \ <7>
> match ip tos 0x10 0xff \ <8>
> flowid 1:6 \ <9>
> police \ <10>
> rate 32000bps \ <11>
> burst 10240 \ <12>
> mpu 0 \ <13>
> action drop/continue <14>
```
1. 新增一個過濾器,動作也可以為`del`
2. 指定附加的新過濾器的裝置
3. 指定附加的新過濾器的父控制代碼
4. 該引數是必須的
5. *`prio`* 引數允許給定的過濾器優先於另一個過濾器
6. 這是一個分類器,是每個tc過濾器命令中必需的部分。
7. 8. 這些是分類器的引數。這種情況將選擇具有tos型別(用於互動使用)和匹配埠22的資料包。
9. *`flowid`* 指定了目標類(或qdisc)的控制代碼,匹配的過濾器應該將選定的資料包傳送到該類。
10. 這是一個策略器,這是每個tc過濾器命令可選的部分
11. 策略器會在速率達到某一個值後執行一個動作,並在速率低於某一值時執行另一個動作
12. 突發與HTB中的突發類似(突發是桶的概念)。
13. 最小的策略單元,為了計算所有的流量,使用的`mpu`為0
14. action表示當`rate`與策略器的屬性匹配時將會執行那些操作。第一個欄位指定了當超過策略器後的動作,第二個欄位指定了其他情況下的動作
如上所示,即使對於上述簡單的例子來說,**tc**命令列工具的語法也是晦澀難懂的,如果說存在一種更簡單的方法來配置Linux流量控制,對於讀者來說,應該不會感到驚訝。參見下一節[Section 5.3, “**tcng**, Traffic Control Next Generation”](https://tldp.org/en/Traffic-Control-HOWTO/ar01s05.html#s-tcng)。
### 5.3 tcng下一代流量控制
參見 [Traffic Control using tcng and HTB HOWTO](http://tldp.org/HOWTO/Traffic-Control-tcng-HTB-HOWTO/) 以及 [tcng 文件](http://linux-ip.net/gl/tcng/)。
下一代流量控制(tcng)為Linux提供了所有流量控制的能力。
### 5.4 Netfilter
Netfilter 是Linux核心提供的一個框架,允許使用自定義的格式來實現各種與網路有關的操作。Netfilter 為報文過濾、網路地址轉換、埠轉換等提供了多種功能和操作,這些功能包括在網路中重定向報文所需的功能,以及提供禁止報文到達計算機網路中的敏感位置的功能。
Netfilter 為一組Linux核心鉤子,允許特定的核心模組將回調函式註冊到核心的網路棧上。這些函式通常會用於流量的過濾和規則的修改,當報文經過網路棧的各個鉤子時都會呼叫這些函式。
#### 5.4.1 iptables
iptables是一個使用者空間的應用程式,允許系統管理員配置由Linux核心防火牆(由不同的Netfilter 實現)提供的表以及其儲存的鏈和規則。不同的核心模組和程式目前用於不同的協議,iptables用於IPv4,ip6tables用於IPv6,arptables用於ARP,ebtables用於以太幀。
iptables需要提升到特權才能執行,並且必須由root使用者執行,否則無法執行。在大多數Linux系統上,iptables安裝在/usr/sbin/iptables 下,且有對應的`man`文件。此外iptables安裝還可能安裝在/sbin/iptables下,但相比於一個基本的二進位制可執行檔案,iptables更像一個服務,因此最好將其保留在/usr/sbin下面。
術語iptables也通常用於指核心級的元件。x_tables是核心模組的名稱,其中包含四個模組所使用的共享程式碼部分,這些模組還提供了用於擴充套件的API。後來,Xtables或多或少被用來指整個防火牆(v4、v6、arp和eb)體系結構。
Xtables 允許系統管理員定義包含處理報文的規則的表。每個表都與一個不同型別的報文處理相關聯。報文會按照順序通過鏈中的規則來處理。鏈中的一個規則可能會跳轉到另外一個鏈,通過這種方式可以做到任意級別的巢狀。每個到達或離開計算機的報文都會經過至少一個鏈。
![](https://img2020.cnblogs.com/blog/1334952/202011/1334952-20201117093547485-520285800.png)
報文的源可以決定該報文首先進入哪個鏈。iptables中預定義了五個鏈(對應五個Netfilter 鉤子),但不是每個表都包含所有的鏈。
![](https://img2020.cnblogs.com/blog/1334952/202011/1334952-20201117093642865-2009778594.png)
預定義的鏈都有一個策略,如DROP,即當報文到達鏈尾時會執行丟棄動作。系統管理員可以按照需要建立任意多的鏈,新建立的鏈並沒有任何策略,當報文到達鏈尾時,會返回到呼叫該鏈的位置。一個鏈也可能是空的。
- `PREROUTING:` 報文在進行路由處理前會進入該鏈。
- `INPUT:` 報文會上送到本地,它與本地開啟的socket沒有任何關係。本地上送的邏輯由"本地上送"路由表控制。可以使用`ip route show table local`命令檢視
- `FORWARD:` 所有已經路由到非本地的報文將會經過該鏈
- `OUTPUT:` 本機發送的報文會經過該鏈
- `POSTROUTING:` 在完成路由決策後,報文傳遞給硬體之前進入這個鏈
鏈中的每個規則都包含報文匹配的規範,還有可能包含目標(target ,用於擴充套件)或判定(verdict ,內建決策之一)。當一個報文進入一條鏈後,會按照順序逐一檢查鏈中的每條規則,如果這條規則不匹配,則檢查下一條規則。如果一個規則匹配報文,那麼就會按照規則中的目標/判定指定的動作來處理該報文,執行的結果可能會允許或拒絕繼續在鏈中處理報文。由於匹配包含了報文檢測的條件,因此其佔了規則集的絕大部分。這些匹配場景可能發生在OSI模型的任何層,例如——mac-source和-p tcp——dport引數,此外也可以包含獨立於協議的匹配規則,如-m時間。
報文會繼續在鏈中處理,直到發生下面任意一種情況:
- 匹配到一條規則(且該規則決定了報文的最終命運,如呼叫了`ACCEPT`或`DROP`),或使用了一個決定報文最終命運的模組。
- 規則呼叫了`RETURN`,導致處理返回到呼叫鏈
- 到達鏈尾,後續會在父鏈中繼續處理(如果使用了RETURN),或基於鏈策略處理。
目標也會返回類似ACCEPT(NAT模組會這麼做)或DROP(如REJECT模組)的判定,但也可能會暗示CONTINUE(如LOG模組;CONTINUE是一個內部名稱)來繼續處理下一個規則(如果沒有指定任何目標/判定)。
### 5.5 IMQ,中間佇列裝置
中間佇列裝置並不是qdisc,但它的用法與qdiscs緊密相關。在Linux中,qdisc會附加到網路裝置上,任何要進入裝置的報文,首先會進入qdisc,然後才會進入驅動佇列。從這個觀點看,會有兩個限制:
- 只能做egress整流(雖然存在ingress qdisc,但相對於classful qdisc來說,其功能非常有限)
- 一個qdisc只能看到一個介面的流量,無法做全侷限制
IMQ用於解決這兩大限制。簡單地說,可以將任意選定的內容放到一個qdisc中。特殊標記的報文會在netfilter NF_IP_PRE_ROUTING 和NF_IP_POST_ROUTING 鉤子中被攔截,並通過附加的qdisc傳遞給imq裝置。iptables目標可以用於標記報文。
這種方式允許對ingress流量進行整流,只要標記來自某處的報文,並/或將介面當作類來設定全域性的限制。此外做很多其他的事情,比如把http流量放到qdisc中,把新的連線請求放到qdisc中等。
#### 5.5.1 配置示例
下面使用ingress整流來授權一個高頻寬。對其他介面的配置類似:
```shell
tc qdisc add dev imq0 root handle 1: htb default 20
tc class add dev imq0 parent 1: classid 1:1 htb rate 2mbit burst 15k
tc class add dev imq0 parent 1:1 classid 1:10 htb rate 1mbit
tc class add dev imq0 parent 1:1 classid 1:20 htb rate 1mbit
tc qdisc add dev imq0 parent 1:10 handle 10: pfifo
tc qdisc add dev imq0 parent 1:20 handle 20: sfq
tc filter add dev imq0 parent 10:0 protocol ip prio 1 u32 match \ ip dst 10.0.0.230/32 flowid 1:10
```
本例使用u32來分類。此外還可以使用其他分類器,後續的流量將會被選擇並打上標記,最後入佇列到imq0。
```
iptables -t mangle -A PREROUTING -i eth0 -j IMQ --todev 0
ip link set imq0 up
```
在iptables的mangle表的PREROUTING 和POSTROUTING 鏈中可以配置IMQ目標。語法為:
```
IMQ [ --todev n ] n : number of imq device
```
也支援ip6tables目標。
注意,流量不是在目標被匹配時進入佇列,而是在匹配之後。當流量進入imq裝置之後的具體位置取決於流量的方向(進/出)。這些位置由iptables預定義的netfilter 鉤子決定。
```
enum nf_ip_hook_priorities {
NF_IP_PRI_FIRST = INT_MIN,
NF_IP_PRI_CONNTRACK = -200,
NF_IP_PRI_MANGLE = -150,
NF_IP_PRI_NAT_DST = -100,
NF_IP_PRI_FILTER = 0,
NF_IP_PRI_NAT_SRC = 100,
NF_IP_PRI_LAST = INT_MAX,
};
```
對於ingress流量,imq會使用`NF_IP_PRI_MANGLE + 1` 的優先順序,意味著在處理完PREROUTING 鏈之後,報文會直接進入imq裝置。
對於使用NF_IP_PRI_LAST 的egress流量,意味著,被過濾表丟棄的報文將不會佔用頻寬。
### 5.6 ethtool,驅動佇列
ethtool 命令用於控制以太介面的驅動佇列大小。ethtool 也提供了底層介面的資訊以及啟用/禁用IP棧和驅動特性的能力。
ethtool 的-g標誌可以展示驅動佇列(ring)的引數:
```
$ethtool -g eth0
Ring parameters for eth0:
Pre-set maximums:
RX: 16384
RX Mini: 0
RX Jumbo: 0
TX: 16384
Current hardware settings:
RX: 512
RX Mini: 0
RX Jumbo: 0
TX: 256
```
可以看到該NIC的驅動的輸出佇列為256個描述符。為了減少延遲,通常建議降低驅動佇列的大小。在引入BQL(假設NIC驅動支援)之後,就不需要修改驅動隊列了。
ethtool還可以管理如 [TSO, UFO 和GSO](https://tldp.org/en/Traffic-Control-HOWTO/ar01s02.html#o-huge-packet)這樣的優化特性。`-k`表示展示了當前設定的解除安裝,可以使用`-K`進行修改。
```
$ethtool -k eth0
Offload parameters for eth0:
rx-checksumming: off
tx-checksumming: off
scatter-gather: off
tcp-segmentation-offload: off
udp-fragmentation-offload: off
generic-segmentation-offload: off
generic-receive-offload: on
large-receive-offload: off
rx-vlan-offload: off
tx-vlan-offload: off
ntuple-filters: off
receive-hashing: off
```
由於[TSO, GSO, UFO](https://tldp.org/en/Traffic-Control-HOWTO/ar01s02.html#o-huge-packet)和GRO通常會增加驅動佇列中的報文位元組數,如果需要優化延遲(而非吞吐量),建議關閉這些特性。除非系統正在處理非常高速率的資料,否則禁用這些特性時,可能不會注意到任何CPU影響或吞吐量