在开发和测试网络相关应用时,经常需要模拟低速网络环境来验证程序在弱网下的表现。Fedora作为内核更新激进的发行版,自带了完整的流量控制(Traffic Control)子系统,通过tc命令就能对任意网卡进行带宽限速、延迟注入、丢包模拟等操作,无需安装额外的商业工具。本文将系统地介绍tc限速的核心原理和在Fedora上的具体操作方法。

一、tc限速的核心原理:令牌桶与HTB
要理解tc的工作方式,首先要明白令牌桶算法。想象一个固定容量的桶,系统以恒定速率往桶里放入令牌,数据包发送前必须从桶中取出令牌,桶空时数据包就只能排队等待。控制令牌的生成速率,就等于控制了数据的平均发送速率;桶的大小则决定了允许的突发流量上限。tc中的TBF(Token Bucket Filter)就是最简单的令牌桶实现,适合对单个网卡做整体限速。
实际使用中更常用的是HTB(Hierarchical Token Bucket,分层令牌桶)。HTB允许把带宽划分成多层级的类别,父类总带宽可以被多个子类共享,子类之间可以设置优先级,未被占用的带宽还能借给其他子类使用。例如你有一条100Mbps的链路,可以划出30Mbps给下载业务、10Mbps给备份流量,剩余带宽弹性分配,这种灵活性是TBF不具备的。
在tc的架构中,还有两个重要概念:filter(过滤器)和qdisc(排队规则)。qdisc决定数据包如何排队和发送,HTB本身就是一种classful qdisc;filter则负责把特定的数据包(比如某个IP、某个端口)分类到对应的子类中,实现精细化控制。理解这三者的关系,是写tc命令的基础。
二、Fedora下tc的基本安装与限速实操
tc命令包含在iproute软件包中,Fedora默认已经安装。如果没有,可以通过dnf安装:
# 检查tc是否可用 tc -V # 如果提示未找到命令,安装iproute sudo dnf install iproute
最简单的限速场景:把网卡的整体出方向带宽限制为10Mbps。假设网卡名为eth0,命令如下:
# 在eth0的根上挂载HTB排队规则 sudo tc qdisc add dev eth0 root handle 1: htb default 10 # 创建一个上限为10Mbps的类别 sudo tc class add dev eth0 parent 1: classid 1:10 htb rate 10mbit burst 15k
第一条命令中的handle 1:为这个qdisc分配了句柄号,default 10表示未被分类的流量进入1:10这个类别。第二条命令中rate 10mbit是持续速率上限,burst 15k是突发桶大小。注意tc只对发出方向生效,如果想限制下载速度,需要把qdisc挂到流入方向的网卡上,常见做法是使用ifb虚拟网卡把入向流量重定向后再限速。
配合Netem还能模拟真实网络的延迟和丢包,这在测试弱网体验时非常有用:
# 在限速基础上叠加100ms延迟和1%丢包 sudo tc qdisc add dev eth0 parent 1:10 handle 20: netem delay 100ms loss 1% # 查看当前规则 tc qdisc show dev eth0 # 删除所有规则,恢复原始状态 sudo tc qdisc del dev eth0 root
三、针对特定IP或端口的精细限速
整体限速往往不够精确,更多时候我们需要只限制某个IP或某个服务的带宽。这时需要用到filter配合u32匹配器。下面的例子把来自192.168.1.100的流量限制在5Mbps,其他流量限制在50Mbps:
# 建立HTB结构
sudo tc qdisc add dev eth0 root handle 1: htb default 20
sudo tc class add dev eth0 parent 1: classid 1:10 htb rate 5mbit
sudo tc class add dev eth0 parent 1: classid 1:20 htb rate 50mbit
# 用u32过滤器把目标为192.168.1.100的流量导入1:10
sudo tc filter add dev eth0 protocol ip parent 1: prio 1 u32 \
match ip dst 192.168.1.100 flowid 1:10
如果只想限制某个端口,比如把SSH的22端口流量导入低带宽类别,把匹配条件换成端口号即可:
sudo tc filter add dev eth0 protocol ip parent 1: prio 1 u32 \
match ip sport 22 0xffff flowid 1:10
需要注意,filter的匹配是有方向性的:match ip dst匹配目标地址,对应出方向的流量;match ip sport匹配源端口,match ip dport匹配目标端口。多条filter按prio值从小到大依次尝试匹配。此外,如果规则较多,u32写法会相当繁琐,可以改用iptables打标记再配合fw过滤器,用MARK值来分流,维护起来更清晰。
四、常见问题排查与规则管理
实际操作中最常见的报错是RTNETLINK answers: File exists,说明根qdisc已经存在,重复执行add命令就会触发。解决办法要么先用tc qdisc del dev eth0 root清理,要么把add换成change修改现有规则。另一个高频问题是限速后速度远低于设定值,这通常是burst设置过小导致的,桶太小会让令牌供应跟不上,适当增大burst即可缓解。
tc规则保存在内存中,重启后自动失效。如果需要持久化,可以把命令写入systemd服务单元或/etc/NetworkManager/dispatcher.d/下的脚本,让网卡启动时自动执行。排查时常用的查看命令有三个:tc qdisc show看排队规则、tc class show dev eth0看类别结构和实际流量统计、tc filter show dev eth0看过滤器命中情况。class输出中的lended和borrowed字段能直观反映带宽借用情况,对调优很有帮助。
最后提醒一点,限速测试尽量在虚拟机或测试机上做,避免在生产环境随意挂载qdisc。HTB本身只控出向流量,入向限速依赖ifb或入口qdisc,机制相对复杂,如果只是模拟弱网给客户端测试,把tc挂在服务端的出方向往往是最简单有效的方案。