tc(traffic control)是 Linux 裡用來做網路流量控制(QoS, Quality of Service)的工具。可針對網路卡,進行限速(rate limiting)、排程(traffic scheduling)、優先權控制(priority)、模擬網路狀況(延遲、丟包、抖動)、封包分類與整形(shaping)的網路封包控制。主要用途是用來模擬網際網路的封包狀況。
流量控制方式
shaping
當流量被shaping限制後,它的傳輸速率就被控制在某個值以下。限制值可以遠小於有效頻寬,這樣可以平滑突發的資料流量,使網路更爲穩定。shaping(限制)只適用於向外的流量。shaping 只能用在 egress 對外的資料輸出。
scheduling
通過調度封包的傳輸,可以在頻寬範圍內,按照優先等級分配頻寬。scheduling 只能用在 egress 對外的資料輸出。
policing
shaping用於處理 egress 對外的流量,而 policing 用於處理 ingress 接收到的流量。
dropping
如果流量超過某個設定的頻寬,就丟棄資料,可用在 egress 及 ingress。
主要元件
qdisc (Queueing Discipline)
佇列規則,決定封包排隊的送出規則。當kernel需要通過某個網絡port發送資料,它都需要按照爲這個port配置的qdisc排隊規則,把資料加入queue。然後kernel會儘可能地從qdisc裏面取出資料,交給網絡適配器驅動模塊。最簡單的qdisc是 pfifo,它不對進入的封包做任何的處理,採用先入先出的方式通過隊列。不過,它會暫存網絡接口短時間內無法處理的封包。
常見的 qdisc:
pfifo_fast(預設)htb(常用做限速)fq_codel(改善 bufferbloat)netem(模擬網路)
class
在 qdisc 底下建立不同流量分類,用來分配不同頻寬。不同的類別中可以包含更深入的QDisc(排隊規則),通過這些細分的QDisc還可以爲進入的隊列的封包排隊。通過設置各種類別封包的離隊次序,QDisc可以設定網路數據流量的優先等級。
filter, policy 過濾器
用來把封包分類到不同 class,決定它們按照何種QDisc進入隊列。分類的方法可以有多種,使用fileter(過濾器)就是其中之一。使用filter(過濾器)分類時,kernel會呼叫附屬於這個 class 的所有過濾器,直到回傳一個判決。如果沒有判決返回,就作進一步的處理,而處理方式和QDISC有關。filter(過濾器)是放在QDisc內部。
指令
tc [OPTIONS] OBJECT COMMAND
常見 OBJECT:
qdisc
class
filter
指令內可使用的資料單位
頻寬或流量單位
kbps mbps kbit mbit bps資料單位
kb mb mbit kbit b時間
s/sec/secs ms/msec/msecs us/usec/usecs
options
-s, -stats, -statistics: output more statistics about packet usage.
-d, -details: output more detailed information about rates and cell sizes.
-r, -raw: output raw hex values for handles.
-p, -pretty: decode filter offset and mask values to equivalent filter commands based on TCP/IP.
-iec: print rates in IEC units (ie. 1K = 1024).
Examples
tc 通常是控制 egress 的流量
查看設定
tc -s qdisc show dev eth0
tc class show dev eth0
tc filter show dev eth0
# tc -s qdisc show dev enp1s0
qdisc mq 0: root
Sent 220264 bytes 431 pkt (dropped 0, overlimits 0 requeues 2)
backlog 0b 0p requeues 2
qdisc fq_codel 0: parent :4 limit 10240p flows 1024 quantum 1514 target 5ms interval 100ms memory_limit 32Mb ecn drop_batch 64
Sent 20103 bytes 107 pkt (dropped 0, overlimits 0 requeues 0)
backlog 0b 0p requeues 0
maxpacket 0 drop_overlimit 0 new_flow_count 0 ecn_mark 0
new_flows_len 0 old_flows_len 0
qdisc fq_codel 0: parent :3 limit 10240p flows 1024 quantum 1514 target 5ms interval 100ms memory_limit 32Mb ecn drop_batch 64
Sent 43974 bytes 148 pkt (dropped 0, overlimits 0 requeues 0)
backlog 0b 0p requeues 0
maxpacket 0 drop_overlimit 0 new_flow_count 0 ecn_mark 0
new_flows_len 0 old_flows_len 0
qdisc fq_codel 0: parent :2 limit 10240p flows 1024 quantum 1514 target 5ms interval 100ms memory_limit 32Mb ecn drop_batch 64
Sent 146267 bytes 124 pkt (dropped 0, overlimits 0 requeues 2)
backlog 0b 0p requeues 2
maxpacket 8925 drop_overlimit 0 new_flow_count 1 ecn_mark 0
new_flows_len 0 old_flows_len 0
qdisc fq_codel 0: parent :1 limit 10240p flows 1024 quantum 1514 target 5ms interval 100ms memory_limit 32Mb ecn drop_batch 64
Sent 9920 bytes 52 pkt (dropped 0, overlimits 0 requeues 0)
backlog 0b 0p requeues 0
maxpacket 0 drop_overlimit 0 new_flow_count 0 ecn_mark 0
new_flows_len 0 old_flows_len 0
# tc -d qdisc show dev enp1s0
qdisc mq 0: root
qdisc fq_codel 0: parent :4 limit 10240p flows 1024 quantum 1514 target 5ms interval 100ms memory_limit 32Mb ecn drop_batch 64
qdisc fq_codel 0: parent :3 limit 10240p flows 1024 quantum 1514 target 5ms interval 100ms memory_limit 32Mb ecn drop_batch 64
qdisc fq_codel 0: parent :2 limit 10240p flows 1024 quantum 1514 target 5ms interval 100ms memory_limit 32Mb ecn drop_batch 64
qdisc fq_codel 0: parent :1 limit 10240p flows 1024 quantum 1514 target 5ms interval 100ms memory_limit 32Mb ecn drop_batch 64
qdisc mq 0: root
根節點 mq : multi-queue
代表你的硬體網路卡有多個傳輸佇列(在這個例子中是 4 個,分別對應
parent :1到:4)。通常發生在多核心 CPU 的伺服器上,系統會將網路流量分散到不同的 CPU 核心處理,以提升效能fq_codel (Fair Queuing Controlled Delay)
這是目前 Linux 系統非常推薦的排隊規則,它由兩個部分組成
FQ (Fair Queuing):確保每個「連線」(Flow)都能公平分配頻寬。例如,即使有人在下載大檔案,你的網頁瀏覽或視訊通話依然能順暢通過。
CoDel (Controlled Delay):這是一個智慧演算法,當它發現緩衝區(Buffer)塞車太久時,會主動丟棄封包或標記 ECN,強制降低延遲,解決「緩衝區膨脹」問題。
以其中一個
fq_codel為例:limit 10240p: 佇列中最多可以存放 10,240 個封包。
flows 1024: 它最多可以同時追蹤並隔離 1024 個不同的連線。
target 5ms / interval 100ms: 這是 CoDel 的核心參數。它會嘗試將延遲控制在 5ms 以內;如果延遲超過這個數值持續 100ms,它就會開始干預流量。
ecn: 支援顯式擁塞通知。當網路塞車時,它會通知發送端減速,而不是直接丟棄封包。
-s 統計
Sent 146267 bytes 124 pkt (dropped 0, overlimits 0 requeues 2)Sent: 該佇列目前傳送的資料量與封包數。
dropped 0: 這是一個好現象。 代表目前沒有封包因為塞車而被強制丟棄。
requeues 2: 封包重新排隊的次數。通常發生在硬體驅動程式暫時無法處理封包時(例如網路卡發送視窗滿了),這在正常運作下是偶爾會發生的。
new_flow_count 1: 代表自從統計開始以來,有一個「新連線」被偵測到。
fq_codel會優先處理新連線(互動式流量),以保證反應速度。
刪除設定
清除所有設定
tc qdisc del dev eth0 root
備份/還原完整設定
# 備份
tc -dump qdisc show dev eth0 > tc-backup.txt
tc -dump class show dev eth0 >> tc-backup.txt
tc -dump filter show dev eth0 >> tc-backup.txt
# 還原前先清空
tc qdisc del dev eth0 root
tc qdisc del dev eth0 ingress
tc -batch tc-backup.txt
完整備份所有網卡
tc -dump qdisc show > tc-all.txt
tc -dump class show >> tc-all.txt
tc -dump filter show >> tc-all.txt
# 還原
tc -batch tc-all.txt
限制整個網卡頻寬
限制 eth0 最大 1Mbps
tc qdisc add dev eth0 root tbf rate 1mbit burst 32kbit latency 400ms
tbf = Token Bucket Filter(簡單限速)
rate = 頻寬
burst = 突發流量
latency = 最大延遲
使用 HTB 做分流
# 建立 root qdisc
tc qdisc add dev eth0 root handle 1: htb default 30
# 建立 class 做頻寬分配
tc class add dev eth0 parent 1: classid 1:1 htb rate 10mbit
tc class add dev eth0 parent 1:1 classid 1:10 htb rate 5mbit
tc class add dev eth0 parent 1:1 classid 1:20 htb rate 2mbit
1: (root)
└── 1:1 (10mbit)
├── 1:10 (5mbit)
└── 1:20 (2mbit)
依 IP 分流
ex
192.168.1.100 → 5Mbps
其他 → 2Mbps
tc filter add dev eth0 protocol ip parent 1:0 prio 1 u32 \
match ip dst 192.168.1.100 flowid 1:10
模擬網路延遲
增加 100ms 延遲
tc qdisc add dev eth0 root netem delay 100ms
模擬丟包
10% 封包丟失
tc qdisc add dev eth0 root netem loss 10%
延遲 + 抖動 + 丟包
tc qdisc add dev eth0 root netem delay 100ms 20ms loss 5%
100ms= 基本延遲20ms= 抖動(jitter)loss 5%= 丟包
控制 ingress
tc qdisc add dev eth0 handle ffff: ingress
ingress shaping ifb
tc 原生只能控制 egress(送出流量) ,要控制 ingress(進來的流量),就要「轉送」到 ifb 再做 shaping
eth0 (ingress)
↓ redirect
ifb0 (egress)
↓ tc 控制
限制下載速度(ingress 10Mbps)
# 載入 ifb 模組
modprobe ifb
# 指定數量
modprobe ifb numifbs=1
# 啟用 ifb0
ip link add ifb0 type ifb
ip link set dev ifb0 up
# 在 eth0 加 ingress qdisc
tc qdisc add dev eth0 handle ffff: ingress
# 把 ingress 流量導到 ifb0
# mirred = mirror/redirect
tc filter add dev eth0 parent ffff: protocol ip u32 \
match u32 0 0 action mirred egress redirect dev ifb0
# 在 ifb0 上做限速
tc qdisc add dev ifb0 root tbf rate 10mbit burst 32k latency 400ms
# show
tc -s qdisc show dev ifb0
clear
tc qdisc del dev eth0 ingress
tc qdisc del dev ifb0 root
ip link delete ifb0
不同 IP 不同下載速度
ex
192.168.1.100 → 5 Mbps
其他 → 2 Mbps
用 HTB
tc qdisc add dev ifb0 root handle 1: htb default 20
tc class add dev ifb0 parent 1: classid 1:1 htb rate 10mbit
tc class add dev ifb0 parent 1:1 classid 1:10 htb rate 5mbit
tc class add dev ifb0 parent 1:1 classid 1:20 htb rate 2mbit
# filter 分流
# ingress → ifb 後. source IP 才是外部來源 IP
tc filter add dev ifb0 protocol ip parent 1: prio 1 u32 \
match ip src 192.168.1.100 flowid 1:10
note: ingress 只能有一個 qdisc