在我的 Linux 路由器中,我使用以下配置来限制 LAN 中客户端的 44444 端口的流量速率(客户端地址为 192.168.10.2,通过路由器的 eth1 iface 连接):
tc qdisc add dev eth1 root handle 1: htb default 2
tc class add dev eth1 parent 1: classid 1:1 htb rate $RATE
tc class add dev eth1 parent 1: classid 1:2 htb rate 100mbit
tc filter add dev eth1 protocol ip parent 1: prio 1 u32 match ip dst 192.168.10.2 dport 44444 0xffff flowid 1:1
我期望这个配置能够让去往 192.168.10.2:44444 的流量根据 $RATE 参数进行整形,而其他所有流量基本保持不变(因为 LAN 是 100Mbit/s)。
为了测试此配置,我以不同的速率向 192.168.10.2:44444 发送 UDP 数据包,跟踪丢失的数据包数量和单向延迟变化。我在测试期间观察到,超过速率的数据包绝不被丢弃。相反,数据包会排队进入一个缓冲区,这个缓冲区会不断增大,但(显然)永远不会达到大小限制。
例如:
使用 RATE=30kbit 并以大约 2Mbit/s 的速度发送数据包(数据包有效负载 1400 字节,数据包间隔 5ms)持续 10 秒,我从 tc 获得以下统计数据:
qdisc htb 1: root refcnt 2 r2q 10 default 2 direct_packets_stat 0 ver 3.17
Sent 104901 bytes 85 pkt (dropped 0, overlimits 185 requeues 0)
backlog 0b 0p requeues 0
(统计数据显示通过tc -s -d qdisc show dev eth1
)
事实上,192.168.10.2 接收数据包的时间超过 26 秒(即发送方完成接收后 16 秒)。
使用 RATE=5mbit 并以 20mbit 的速度发送数据包,我得到以下统计数据:
qdisc htb 1: root refcnt 2 r2q 10 default 2 direct_packets_stat 0 ver 3.17
Sent 6310526 bytes 4331 pkt (dropped 0, overlimits 8667 requeues 0)
backlog 0b 0p requeues 0
尽管这次单向延迟不会超过160毫秒。
我也得到了指定大小的类似结果burst
,但我没有观察到任何显着的变化,无论我将它设置得多低(我将它降低到 1kbit)。
不幸的是,尽管我读过关于 Linux tc 和 htb 的各种手册和参考资料,但我还是找不到这些结果的合理解释。如果有人能帮我解决这个问题,我会很高兴。
谢谢
更新。我找到了一个非常有用且清晰的 Linux 流量控制器内部描述。你可以找到它这里. 另一个有用的资源是OpenWRT 维基百科。其实我已经知道了前一篇文章,但显然我错过了一些重要的部分。
长话短说,我的数据包排队的缓冲区当然是网络接口的出口队列。出口队列中的数据包根据通过命令设置的排队规则进行选择传输tc
。有趣的是,出口队列不是以字节为单位,而是以数据包为单位(无论数据包有多大)。这在一定程度上解释了为什么我在实验中从未达到队列大小限制。
ifconfig
出口队列大小通过命令 (字段)显示txqueue
。在我的 Linux 机器中,默认出口大小为 1000 个数据包,但您可以通过 轻松更改它ifconfig DEV txqueuelen SIZE
。通过将队列大小减小到 1 个数据包,我最终设法强制丢弃整形数据包(永远不会到达客户端)。所以我想基本上就是这样。
我注意到的最后一个有趣的事实是,令牌桶过滤器(tbf
)与层次桶过滤器相反做提供一个额外的缓冲区,数据包在传输之前会先在此缓冲区排队。我猜想使用队列足够小的过滤器可以强制丢弃数据包,无论出口队列有多大。不过我还没有尝试过。
希望这可以帮助。