目录
HTB (Hierarchical Token Bucket)
目前需要做一款关于Linux下对于某IP限制的一个工具(QOS),在网上寻找了许多关于TC的教程,大多数都是一些比较基础的教学,且多数都是对网口的一些限制,然后自己研究了一段时间,最后有一些小小的了解,故在此分享给大家。
首先我们可以先假设把TCP包的架构设成这样,方便我们后续去了解限速的规则。这里一定要记好了。
[source-ip] | [source-port] | [other-data] | [destination-ip] | [destination-port]
简单概述:
TC,即 Traffic Control,是 Linux 内核提供的一种用于网络流量管理和质量服务(Quality of Service, QoS)的工具。它允许网络管理员对网络接口上的数据包进行精细的控制,包括但不限于:
- SHAPING(限制)
- 当流量被限制时,其传输速率被控制在预设的阈值之下。这种限制可以显著减少突发流量,有助于维持网络的稳定性和预测性。SHAPING 主要应用于向外的流量控制。
- SCHEDULING(调度)
- 调度涉及在可用带宽范围内,按优先级分配带宽资源。这确保了关键应用和服务可以得到优先处理,从而提高了网络的整体效率。SCHEDULING 同样适用于向外的流量。
- POLICING(策略)
- POLICING 通常用于控制入站流量,当检测到流量超出预设规则时,可以采取行动,如丢弃超额的数据包,以维持网络政策的一致性。
- DROPPING(丢弃)
- 当流量超出设定的带宽限制时,DROPPING 策略将直接丢弃过量的数据包。这一策略适用于入站和出站流量。
tc
命令的基本结构如下:
Bash
1tc [ OPTIONS ] COMMAND [ @id ] dev DEV [ parent qdisc-id ] [ index INDEX ]
其中 COMMAND
可以是 qdisc
, filter
, class
, action
等不同的子命令,用于执行不同的流量控制任务。
- qdisc(排队规则)
- qdisc 定义了数据包在接口上排队和调度的策略。它决定了数据包如何被存储和发送,是流量控制的核心机制。qdisc 可分为 CLASSLESS QDISC 和 CLASSFUL QDISC。
- CLASSLESS QDISC
- 包括简单的 FIFO(First-In-First-Out)队列,如 pfifo 和 bfifo,以及更复杂的策略如 pfifo_fast、red、sfq 和 tbf。
- pfifo/bfifo:简单的 FIFO 队列,前者基于数据包计数,后者基于字节数。
- pfifo_fast:在高级路由器配置中作为默认 qdisc,具有三个优先级队列。
- red:随机早期检测,用于预防拥塞。
- sfq:基于概率的公平队列,适用于多流场景。
- tbf:令牌桶过滤器,用于限速和整形。
- CLASSFUL QDISC
- 提供更细粒度的控制,允许创建基于类别的队列,如 HTB 和 CBQ,用于复杂的服务质量策略。
- class(类别)
- 类别用于组织和划分流量,允许为不同类型的流量分配特定的带宽和优先级。
- filter(过滤器)
- 过滤器用于识别和分类数据包,基于 IP 地址、端口、协议等属性,将数据包导向特定的 qdisc 或 class。
qdisc(队列):
- 带宽分配:限制或保证数据流的带宽。
- 延迟控制:通过缓存机制控制数据包的发送时间,从而影响网络延迟。
- 丢包策略:当队列满时决定哪些数据包被丢弃的策略,以防止拥塞。
- 公平性:确保多个数据流之间的公平带宽分配。
举例:
Bash
tc qdisc add dev ens18 handle fff: htb default 22
其中fff:就相当于这条队列的特定标识符,也相当于唯一ID。
谈到队列大家可能有些熟悉,是数据结构中的队列吗?先进先出的那个?当然不是,这是由Linux做的一款流量整形队列,你可以把它看作一条支流,对,小溪那种。
在我们正常使用的情况下,其实LInux就已经给我们的网卡分配了一些队列,应该也是用来整形稳定的一种。当我们对网口设置了一个新的队列后,这个队列就会被我的这个队列给顶替下去。
编辑
整形队列算法:
FIFO (First-In-First-Out)
这是最简单的队列纪律类型,没有额外的管理机制。数据包按照它们到达的顺序被发送出去。FIFO 不做任何排序或优先级处理,因此所有数据流都被平等对待。这在大多数情况下是默认的行为,但如果网络负载很高,可能会导致一些数据包的延迟或丢失。
PFIFO (Priority FIFO)
PFIFO 提供了有限数量的优先级队列,通常为三个:高、中、低。这允许网络管理员根据数据包的类型或来源为其分配不同的优先级,从而确保关键数据包(如语音或视频流)得到优先处理。然而,PFIFO 仍然依赖于简单的先进先出原则,只是在不同的优先级队列中。
SFQ (Stochastic Fair Queuing)
SFQ 是一种更复杂的队列管理算法,旨在为每个数据流提供公平的带宽分配,即使在高负载情况下也能保持良好的响应时间。它使用基于概率的算法来分类数据包,然后将它们放入不同的队列中,确保即使小流量的数据流也能获得一定的带宽。
RED (Random Early Detection)
RED 是一种主动丢包机制,用于预防网络拥塞。当队列接近其最大容量时,RED 开始随机丢弃一些数据包,而不是等到队列完全填满。这种提前丢弃数据包的策略有助于平滑网络流量,减少拥塞的发生,从而改善整体网络性能。
HTB (Hierarchical Token Bucket)
HTB 是一种非常灵活的队列纪律,支持复杂的带宽分配和优先级管理。它可以创建层次化的令牌桶,允许网络管理员为不同的数据流设定具体的带宽限制和优先级。HTB 特别适用于需要精细控制网络资源分配的场景,如企业网络和数据中心。
TBF (Token Bucket Filter)
TBF 是基于令牌桶算法的流量整形工具,主要用于限速和流量整形。它允许指定一个固定的带宽速率,确保数据流不会超过这个速率,这对于避免网络拥塞和保证服务质量非常有用。
NETEM (Network Emulator)
NETEM 是一个网络仿真工具,用于模拟各种网络状况,如延迟、丢包和重复。它非常适合在开发和测试环境中模拟真实世界的网络条件,帮助验证应用程序在网络问题下的表现。
CBQ (Class-based Queueing)
CBQ 是早期版本中常用的队列纪律,类似于 HTB,但功能较少且配置更为复杂。它也支持基于类的队列,允许为不同的数据流设置不同的优先级和带宽限制。然而,由于 HTB 的出现,CBQ 在现代系统中的使用已大大减少。
编辑
上方这些算法中,如果你想进行一些简单的流量控制,那么FIFO和PFIFO即可,如果想要进行真正管理级,逻辑稍微复杂一些的流量整控我推荐用HTB,关于HTB它可以进行优先级限制,src和dst IP限制,是一个功能非常强大的算法。SFQ的话是当你建立了多条类的话,用于流量平分用。比如果你从队列里面开出了三条类,也可以理解为引出来了三条小溪流,害怕每条小溪流的水不一样多怎么办?这时候就给每条小溪都设上一个SFQ。
wondershaper源码为例:
编辑
这里其他算法就不过多赘述了,专于对HTB算法进行详细讲解。
整形队列流量方向管理:
- Ingress:表示数据包进入网络设备的方向。当数据包从网络接口接收进来时,这个过程被称为 ingress。在这个阶段,设备可以执行诸如数据包过滤、流量整形、优先级标记等操作。
- Egress:表示数据包离开网络设备的方向。当数据包被发送到另一个网络或设备时,这个过程被称为 egress。在 egress 方向,设备也可能执行类似的操作,如基于策略的路由、QoS 保障、流量限速等。
在 Linux 的 Traffic Control (tc
) 工具中,ingress 和 egress 通常用来描述数据包在网卡或网络设备上的流动方向。例如,当使用 tc
进行流量控制时,你可以分别对 ingress 和 egress 方向的流量应用不同的队列纪律和过滤规则。这点其实是非常重要的,因为TC实际上是管发不管收的。也就是如果我们通过TC只能限制别人对我们的下载速度,没办法限制别人对我们的上传速度。
这个东西在前期确实让人很头痛,那岂不是我们没有办法在自己的机器上对别人进行上传限速?当然不是,Ingress和Egress就可以解决这个问题。这个在后续应用实例的时候去讲。
Class(类) 和 Filter(过滤器):
class
在 tc
中扮演了组织者和控制器的角色。它允许你创建多个类别,每个类别都可以有自己的带宽限制、优先级和调度策略。通过将数据包分配到不同的 class
中,你可以实现以下目标:
- 带宽分配:为每个
class
设置不同的带宽上限,确保每个数据流都得到公平或预定的带宽份额。 - 优先级控制:为关键数据流分配更高的优先级,确保它们在拥塞时仍能快速传输。
- QoS 改善:通过精细调整
class
参数,改善整个网络的服务质量,特别是对于实时应用如 VoIP 和视频会议。
创建 class
class
的创建和配置通常与 qdisc
相结合。首先,你需要在根 qdisc
下创建一个父 class
,然后可以创建子 class
来进一步细分流量。例如,使用 htb
(Hierarchical Token Bucket)作为 qdisc
时,你可以这样创建一个 class
:
Bash
# 创建父 qdisc tc qdisc add dev <interface> root handle 1: htb default 11 # 创建父 class tc class add dev <interface> parent 1: classid 1:1 htb rate <rate> # 创建子 class tc class add dev <interface> parent 1:1 classid 1:11 htb rate <rate> ceil <max_rate>
在上述命令中,<interface>
是你想要控制的网络接口,<rate>
是你想要分配给该 class
的带宽速率,<max_rate>
是该 class
可以使用的最大带宽。
使用 class
和 filter
为了将特定的数据包分配到正确的 class
,你需要使用 filter
。filter
根据数据包的属性(如源/目的IP、端口号、协议类型等)将它们导向特定的 class
。例如:
Bash
tc filter add dev <interface> protocol ip parent 1:0 prio 1 u32 match ip dst <destination_ip>/32 flowid 1:11
此命令创建了一个 filter
,将目的地为 <destination_ip>
的所有 IP 数据包导向 class
1:11
。
问题:
此时以文章最开始的包结构为例,这里的destination_ip就是我们包结构里面的
[destination-ip]。假如由1.1.1.2给1.1.1.3发消息,那我们在1.1.1.3中收到的这个包的目标IP就是1.1.1.3,所以此时我们如果对1.1.1.3进行限速也就是对1.1.1.3进行限速。那如果我直接这个包的SRC进行限速呢?这不就完成了对客户端上传速度的限制?但是大家别忘了,TC是一个管发不管收的工具,既然不管收,那怎么对客户端进行限速呢?
带宽或者流速单位:
编辑
编辑
问题解决方法:
我们可以去将Ingress的流量进行一个导流,然后把导流的流量重定向到一个网卡上,为了稳定这个采用一个虚拟网卡,然后对虚拟网卡上的流量进行限速,就可以解决对收包进行限速了。
限制IP为1.1.1.3上传速度实例:
创建虚拟网卡并启用,暂定虚拟网卡名为ifb0
Bash
modprobe ifb numifbs=1; ip link set dev ifb0 up;
创建Ingress队列并进行导流
Bash
tc qdisc add dev ens18 handle ffff: ingress tc filter add dev ens18 parent ffff: protocol ip u32 match u32 0 0 action mirred egress redirect dev ifb0;
对ifb0进行限速即可
tc qdisc add dev ifb0 root handle 2: htb default 22; tc class add dev ifb0 parent 2: classid 2:22 htb rate 10000mbit ceil 10000mbit burst 10000mbit cburst 10000mbit; tc class add dev ifb0 parent 2: classid 2:1 htb rate 10mbit ceil 10mbit burst 10mbit cburst 10mbit ; tc filter add dev ifb0 protocol ip parent 2: prio 1 u32 match ip src 1.1.1.3 flowid 2:1;
如果想要限制其他的IP将IP改一下即可。
限制IP为1.1.1.3下载速度实例:
正常创建Htb队列,default 22 就是如果没有合适的filter去匹配的话,那就就去找classid为22的类去限速。
tc qdisc add dev ens18 root handle 1:0 htb default 22
创建限速类,这里22限制为10GB每秒,相当于未限速,然后对1.1.1.3限速为10mbit每秒。
tc class add dev ens18 parent 1:0 classid 1:1 htb rate 10mbit ceil 10mbit tc class add dev ens18 parent 1:0 classid 1:22 htb rate 10000mbit ceil 10000mbit # dst->src tc filter add dev ens18 parent 1:0 protocol ip prio 1 u32 match ip dst 1.1.1.3 flowid 1:1
u32匹配:
在Linux的tc
(Traffic Control)工具中,u32
是一种匹配器类型,用于基于数据包头部的信息来过滤和分类网络流量。u32
匹配器非常灵活,可以依据各种字段(如IP地址、端口号、协议类型等)来创建复杂的匹配规则,是实现高级流量控制策略的关键组件。
u32
匹配器的特点
- 灵活性:
u32
匹配器可以基于多种数据包头部字段进行匹配,包括但不限于IP源地址、目的地址、TCP/UDP源端口、目的端口、协议类型等。
- 复杂规则支持:
u32
支持逻辑运算,如AND、OR、NOT,允许创建复合的匹配规则,以适应复杂的网络策略需求。
- 性能高效:
u32
匹配器设计得非常高效,能够快速处理大量的数据包,即使在高负载的网络环境下也能保持良好的性能
- 基于IP地址匹配
- 源IP地址匹配:
Bash
sudo tc filter add dev [interface] protocol ip parent 1:0 prio 1 u32 match ip src [source_ip] flowid 1:1
- 目的IP地址匹配:
Bash
sudo tc filter add dev [interface] protocol ip parent 1:0 prio 1 u32 match ip dst [destination_ip] flowid 1:1
- 基于端口号匹配
- TCP源端口号匹配:
Bash
sudo tc filter add dev [interface] protocol tcp parent 1:0 prio 1 u32 match tcp sport [source_port] flowid 1:1
- TCP目的端口号匹配:
Bash
sudo tc filter add dev [interface] protocol tcp parent 1:0 prio 1 u32 match tcp dport [destination_port] flowid 1:1
- UDP源端口号匹配:
Bash
sudo tc filter add dev [interface] protocol udp parent 1:0 prio 1 u32 match udp sport [source_port] flowid 1:1
- UDP目的端口号匹配:
Bash
sudo tc filter add dev [interface] protocol udp parent 1:0 prio 1 u32 match udp dport [destination_port] flowid 1:1
- 基于协议类型匹配
- 匹配TCP协议:
Bash
sudo tc filter add dev [interface] protocol tcp parent 1:0 prio 1 u32 flowid 1:1
- 匹配UDP协议:
Bash
sudo tc filter add dev [interface] protocol udp parent 1:0 prio 1 u32 flowid 1:1
- 匹配ICMP协议:
Bash
sudo tc filter add dev [interface] protocol icmp parent 1:0 prio 1 u32 flowid 1:1
限速总结:
对于上传和下载下载的规律是不变的,后续filter跟的一些匹配协议就由大家自由发挥了,例如端口,源端口,协议类型等一些东西都是非常多的,在此我也不一一列举了,由大家自由发挥。