我在我的 VPS 上运行 CentOS 7,我想限制特定端口上的带宽。我进行了广泛的调查,在我能找到的解决方案中,要么是对接口的限制,要么是描述模糊的 iptable 设置,似乎只在 CentOS 6 上尝试过。
就我而言,我的 Shadowsocks(代理应用程序)服务器端正在侦听端口1080
、1081
和。我希望允许无限带宽,但将和限制在 1MBps 左右。由于它是代理应用程序,入站和出站流量大致相等。请注意,它是 Shadowsocks 的单个实例,监听 3 个端口,1082
eth0
1080
1081
1082
不是3 个实例各自监听 1 个端口,因此按进程限制带宽不适用。
但除此之外,我可以考虑任何解决方案,无论是 CentOS 开箱即用的支持,还是某种中间监控层。只要能完成工作,我就愿意。
提前致谢。
答案1
只能使用以下方式限制流量Linux的流量控制。
只是为了澄清,影子袜子创建一条隧道,一侧作为 SOCKS5 代理(sslocal
,我假设这就是考虑到给定端口的 OP 服务器上运行的内容),与远程端点通信(ssserver
)它本身将与实际的目标服务器进行通信。 Shadowsocks 处理 SOCKS5 UDP ASSOCIATE,然后在与 (SOCKS5) TCP 端口相同的端口上使用 (SOCKS5) UDP。
此解决方案对 TCP 和 UDP 均按原样工作(请参阅注释 1),但 UDP 可能会带来额外的挑战:如果源正在创建“大于 MTU”大小的 UDP 数据包(这可能不应该由行为良好的客户端或服务器),它们变得支离破碎。TC,有效早于网络过滤器在入口并晚于网络过滤器在出口,将会看到碎片。 UDP 端口在片段中不可用,因此没有过滤器能够捕获它们,并且几乎不会发生任何限制。 TCP 自然地使用 MTU 来限制数据包大小(并且无论如何都会进行路径 MTU 发现)在大多数设置中不会遇到此问题。
这是一个数据包流 ascii 图片(整个图片通常代表一个客户端活动,导致两个流,一个在代理的左侧,一个在代理的右侧):
traffic controlled TCP self-adjusting / no UDP control
-------------> <-------------
/ \ / \
clients | | proxy | | remote ====== real servers
\ / (sslocal) \ / (ssserver)
<------------- ------------->
traffic controlled already rate limited
无需或无需担心远程服务器的流量:
- 从代理到远程服务器的传出当然会受到客户端传入的限制,
- 从远程/服务器传入代理
- TCP 通常会像客户端的流量一样进行调整和行为。
- UDP就不会有这种可能性,除非应用协议能做到。例如:如果通过简单 UDP 的两个视频源从服务器端到达并超出客户端的限制,则两个客户端流都可能会损坏。应该有一个应用程序反馈来减少带宽,这超出了这个范围。
无论如何,它将变得更加复杂,可能涉及shadowsocks内部的更改,以将远程/服务器端流量链接到客户端TC用法。
对于仅发送数据的 SOCKS5 客户端,限制入口需要限制带宽,对于仅接收数据的 SOCKS5 客户端,限制出口他们需要限制带宽:除非所使用的应用程序是众所周知的,否则两种方式都应该受到流量控制。
交通控制是一个复杂的话题,我几乎无法触及。我将给出两种答案:一种是简单粗暴的,仅进行监管(丢弃多余的数据),另一种是更复杂的,进行整形(包括在必须丢弃之前进行延迟),并使用 IFB 接口来解决以下限制:入口。
应阅读以下文档以了解概念和 Linux 实现:
http://www.tldp.org/HOWTO/Traffic-Control-HOWTO/
此外,这个在 shell 脚本中实现的命令(并使用与此答案中类似的机制)也确实可以创造奇迹:
https://github.com/magnific0/wondershaper
简陋
A警察action 用于丢弃任何与端口匹配的多余数据包(这是一种粗略的方法)。它通常用于入口但适用于出口也。流量受到速率限制,但在各种速率受限的客户端之间可能存在波动和不公平共享(特别是涉及 UDP 与 TCP 时)。
出口(传出数据包)
最简单的队列盘允许附加过滤器是普里奥 队列盘,其特定功能不会真正被使用。
tc qdisc add dev eth0 root handle 1: prio
只需为每个端口(意思是“源端口”)添加以下过滤器(8mbits/s <=> 1MBytes/s)
u16 at 0 layer transport
,即可完成 TCP 和 UDP 的过滤(另请参阅注释 2):tc filter add dev eth0 parent 1: protocol ip basic match 'cmp(u16 at 0 layer transport eq 1081)' action police rate 8mibit burst 256k tc filter add dev eth0 parent 1: protocol ip basic match 'cmp(u16 at 0 layer transport eq 1082)' action police rate 8mibit burst 256k
万一我误解了,1081 和 1082 应该只有一个共同限制,请使用它而不是上面的两个,将它们分组在同一个操作中(这很容易使用基本的/电子匹配filter),然后它将在单个令牌桶中处理它们:
tc filter add dev eth0 parent 1: protocol ip basic match 'cmp(u16 at 0 layer transport eq 1081) or cmp(u16 at 0 layer transport eq 1082)' action police rate 8mibit burst 256k
入口(传入数据包)
入口比出口(不能做成型),但无论如何,在简单的情况下都没有完成。使用它只需要添加一个
ingress
qdisc (参见注释 3):tc qdisc add dev eth0 ingress
等效过滤器(
u16 at 2 layer transport
意思是“目标端口”):tc filter add dev eth0 ingress protocol ip basic match 'cmp(u16 at 2 layer transport eq 1081)' action police rate 8mibit burst 256k tc filter add dev eth0 ingress protocol ip basic match 'cmp(u16 at 2 layer transport eq 1082)' action police rate 8mibit burst 256k
或者对于单个限制,而不是上面的两个:
tc filter add dev eth0 ingress protocol ip basic match 'cmp(u16 at 2 layer transport eq 1081) or cmp(u16 at 2 layer transport eq 1082)' action police rate 8mibit burst 256k
清洁 tc
出口,入口或者这两个设置都可以替换为下面的改进版本。应首先清除以前的设置。
要删除以前应用的 tc 设置,只需删除根和入口 队列盘。它们下面的所有内容(包括过滤器)也将被删除。默认界面根目录队列盘保留句柄 0:将被放回。
tc qdisc del dev eth0 root
tc qdisc del dev eth0 ingress
使用有类 qdisc 和 IFB 接口进行更复杂的设置
指某东西的用途成型,这可以在必须丢弃数据包之前延迟数据包,从而提高整体结果。层次令牌桶(HTB),有类 qdisc 将处理带宽,而在它下面的随机公平队列(SFQ)将提高客户端在受限带宽内竞争时的公平性。
出口
这是描述接下来设置的 ASCII 图片:
root 1: HTB classful qdisc | / | \ / | \ / | \ / | \ / 1:20 1:30 HTB classes / 8mibit 8mibit / | \ / | \ / 20: 30: / SFQ SFQ still 1: default port port incl. port 1080 1081 1082
有限的带宽不会借用额外的可用流量(OP 没有要求):这就是为什么它们不是“全部可用带宽”默认类别的子类。剩下的默认流量,包括1080端口,只是停留在1:,没有做特殊处理。在允许类借用可用带宽的不同设置中,这些类应置于其速率设置为最大可用带宽的准确值的父类之下,以便知道借用什么。因此,配置需要针对每种情况进行微调。我保持简单。
htb 有类 qdisc:
tc qdisc add dev eth0 root handle 1: htb
htb 类、附加的 sfq 以及指向它们的过滤器:
tc class add dev eth0 parent 1: classid 1:20 htb rate 8mibit tc class add dev eth0 parent 1: classid 1:30 htb rate 8mibit tc qdisc add dev eth0 parent 1:20 handle 20: sfq perturb 10 tc qdisc add dev eth0 parent 1:30 handle 30: sfq perturb 10 tc filter add dev eth0 parent 1: protocol ip prio 1 basic match 'cmp(u16 at 0 layer transport eq 1081)' flowid 1:20 tc filter add dev eth0 parent 1: protocol ip prio 1 basic match 'cmp(u16 at 0 layer transport eq 1082)' flowid 1:30
或者对于单个限制,而不是上面的 6 个命令:
tc class add dev eth0 parent 1: classid 1:20 htb rate 8mibit tc qdisc add dev eth0 parent 1:20 handle 20: sfq perturb 10 tc filter add dev eth0 parent 1: protocol ip prio 1 basic match 'cmp(u16 at 0 layer transport eq 1081)' flowid 1:20 tc filter add dev eth0 parent 1: protocol ip prio 1 basic match 'cmp(u16 at 0 layer transport eq 1082)' flowid 1:20
入口
入口qdisc 不能用于成型(例如延迟数据包)但只是像简单情况一样用过滤器丢弃它们。为了更好的控制,有一个技巧:中间功能块,它看起来像一个人造的出口接口在哪里入口交通还可以重定向与过滤器,但与网络堆栈的其余部分几乎没有交互。一旦到位,出口考虑到传入流量的真正控制权并不掌握在接收系统手中,因此可以对其应用某些功能,即使其中一些功能可能并不总是有帮助。所以在这里我设置了
ifb0
界面然后复制上面(出口)对其进行设置,使某种入口整形的表现比仅仅监管更好。创造如果b0 (见注4)并应用与之前相同的设置出口:
ip link add name ifb0 type ifb 2>/dev/null || : ip link set dev ifb0 up tc qdisc add dev ifb0 root handle 1: htb
指向它们的类和过滤器:
tc class add dev ifb0 parent 1: classid 1:20 htb rate 8mibit tc class add dev ifb0 parent 1: classid 1:30 htb rate 8mibit tc qdisc add dev ifb0 parent 1:20 handle 20: sfq perturb 10 tc qdisc add dev ifb0 parent 1:30 handle 30: sfq perturb 10 tc filter add dev ifb0 parent 1: protocol ip prio 1 basic match 'cmp(u16 at 2 layer transport eq 1081)' flowid 1:20 tc filter add dev ifb0 parent 1: protocol ip prio 1 basic match 'cmp(u16 at 2 layer transport eq 1082)' flowid 1:30
或者对于单个限制,如果使用上面的 6 个命令:
tc class add dev ifb0 parent 1: classid 1:20 htb rate 8mibit tc qdisc add dev ifb0 parent 1:20 handle 20: sfq perturb 10 tc filter add dev ifb0 parent 1: protocol ip prio 1 basic match 'cmp(u16 at 2 layer transport eq 1081)' flowid 1:20 tc filter add dev ifb0 parent 1: protocol ip prio 1 basic match 'cmp(u16 at 2 layer transport eq 1082)' flowid 1:20
重定向自以太网0的入口到如果b0 出口下面完成了。为了优化,仅重定向预期端口而不是所有流量。实际的过滤和整形是在上面完成的如果b0反正。
tc qdisc add dev eth0 ingress tc filter add dev eth0 ingress protocol ip basic match 'cmp(u16 at 2 layer transport eq 1081)' action mirred egress redirect dev ifb0 tc filter add dev eth0 ingress protocol ip basic match 'cmp(u16 at 2 layer transport eq 1081)' action mirred egress redirect dev ifb0
笔记:
1. 在 Debian 10 / kernel 5.3 上使用一些网络命名空间进行测试。命令语法也在 CentOS 7.6 容器/内核 5.3(而不是 3.10)上进行了测试。
2.u32 match ip sport 1081 0xffff
本来可以用来匹配源端口 1081。但它无法处理 IP 选项的存在。u32 match tcp src 1081 0xffff
可以处理它,但实际上需要复杂的使用三 u32过滤器的解释如下手册页。所以我basic match
最后选择了。
3.无论指定与否,ingress
都有保留句柄ffff:
(指定的句柄值将被忽略),所以我宁愿不指定它。引用 ingress byparent ffff:
可以替换为 just ingress
,这就是我选择的。
4. 第一次创建IFB接口时,会加载ifb模块,默认情况下会自动创建如果b0和如果b1初始命名空间中的接口,如果询问接口名称 ifb0,则会导致错误,而实际上它是作为命令的结果创建的。同时,如果只是加载模块,则该接口不会出现在网络命名空间(例如:容器)中,因此仍然需要该接口。因此添加2>/dev/null || :
可以解决这两种情况。当然,我假设 IFB 支持确实可用。