我在我的 VPS 上运行 CentOS 7,我想限制特定端口的带宽。我环顾四周,在我能找到的解决方案中,要么是对界面的限制,要么是模糊描述的 iptable 设置,似乎只在 CentOS 6 上尝试过。
就我而言,我的 Shadowsocks(代理应用程序)服务器端正在侦听 port 1080
、1081
和1082
on eth0
。我想允许1080
无限带宽,但同时限制1081
在1082
1MBps 左右。由于它是代理应用程序,因此入站和出站流量大致相等。请注意,它是一个监听 3 个端口的 Shadowsocks 实例,而不是每个监听 1 个端口的 3 个实例,因此按进程限制带宽不适用。
但除此之外,任何解决方案对我来说都是可行的,无论是 CentOS 开箱即用的支持,还是某种中间监控层。只要它完成工作,我就愿意接受。
提前致谢。
只能使用Linux 的 Traffic Control来限制流量。
只是为了澄清一下,shadowsocks创建一个隧道,一侧作为 SOCKS5 代理(
sslocal
考虑到给定端口,我假设这是在 OP 服务器上运行的),与远程端点(ssserver
)通信,该端点本身将与实际目标通信服务器。shadowsocks 处理 SOCKS5 UDP ASSOCIATE,然后在与 (SOCKS5) TCP 端口相同的端口上使用 (SOCKS5) UDP。此解决方案对 TCP 和 UDP 均按原样(见注 1)工作,但 UDP 可能会带来额外的挑战:如果源正在创建“大于 MTU”大小的 UDP 数据包(这可能不应该由行为良好的客户端或服务器),它们会变得支离破碎。tc,它在ingress中比netfilter更早工作,在egress中比netfilter晚,将看到这些片段。UDP 端口在片段中不可用,因此没有过滤器能够捕获它们,并且几乎不会发生限制。TCP 自然地使用 MTU 来限制数据包大小(并且无论如何都要进行路径 MTU 发现)在大多数设置中不会遇到这个问题。
这是一个数据包流 ascii 图片(整个图片通常表示一个客户端活动导致两个流,一个在代理的左侧,一个在代理的右侧):
无需担心与远程服务器的流量:
无论如何,将远程/服务器端流量链接到客户端以供tc使用会变得更加复杂,可能涉及 shadowsocks 内部的更改。
对于只发送数据的 SOCKS5 客户端,需要限制来自它们的入口以限制带宽,对于只接收数据的 SOCKS5 客户端,需要限制它们的出口以限制带宽:除非正在使用的应用程序是众所周知的,否则两种方式都应该是流量控制的.
交通控制是一个复杂的话题,我几乎无法触及。我将给出两种答案:一种简单粗暴的只做监管(丢弃过量),一种更复杂的,做整形(包括在不得不丢弃之前延迟),使用 IFB 接口来解决入口限制.
应阅读以下文档以了解概念和 Linux 实现:
http://www.tldp.org/HOWTO/Traffic-Control-HOWTO/
此外,在 shell 脚本中实现的这个命令(并使用与此答案中类似的机制)也确实可以创造奇迹:
https://github.com/magnific0/wondershaper
简陋
策略操作用于丢弃任何多余的数据包匹配端口(这是一种粗略的方法)。它通常用于入口,但也适用于出口。流量是速率受限的,但在各种速率受限的客户端之间可能存在波动和不公平的共享(尤其是在涉及 UDP 与 TCP 的情况下)。
出口(传出数据包)
允许附加过滤器的最简单的qdisc是prio qdisc ,它的特定功能不会真正被使用。
只需为每个端口添加一个以下过滤器(8mbits/s <=> 1MBytes/s)(
u16 at 0 layer transport
表示“源端口”),即可完成 TCP 和 UDP (另请参见注释 2):如果我误解了 1081 和 1082 应该只有一个共同的限制,请使用它而不是上面的两个,将它们分组在同一个操作中(使用basic / ematch过滤器很容易),然后将处理它们单个令牌桶:
入口(传入数据包)
Ingress比egress更受限制(不能进行整形),但无论如何都没有在简单的情况下完成。使用它只需要添加一个
ingress
qdisc (见注 3):等效过滤器(
u16 at 2 layer transport
表示“目标端口”):或单个限制,而不是上述两个:
清洁 tc
egress,ingress或这两种设置都可以替换为下面的改进版本。应先清理以前的设置。
要删除以前应用的 tc 设置,只需删除root和ingress qdiscs。它们下方的所有内容,包括过滤器,也将被删除。具有保留句柄 0:的默认接口根qdisc将被放回。
使用类 qdiscs 和 IFB 接口进行更复杂的设置
使用shape可以在必须丢弃数据包之前延迟数据包,应该会改善整体结果。Hierarchy Token Bucket ( HTB ),一个有类的 qdisc 将处理带宽,而在它之下,随机公平队列 ( SFQ ) 将提高客户端之间在受限带宽内竞争时的公平性。
出口
这是描述下一个设置的 ascii 图片:
有限的带宽不会借用额外的可用流量(OP 没有要求):这就是为什么它们不是“整个可用带宽”默认类的子类。剩下的默认流量,包括 1080 端口,只是停留在 1:,不做特殊处理。在允许类借用可用带宽的不同设置中,应将这些类放在其速率设置为最大可用带宽的准确值的父类之下,以了解借用什么。因此,配置需要针对每种情况进行微调。我保持简单。
htb 有类 qdisc:
htb 类、附加的 sfq 和指向它们的过滤器:
或单个限制,而不是上面的 6 个命令:
入口
入口qdisc 不能用于整形(例如延迟数据包),而只能使用过滤器将它们丢弃,就像在简单情况下一样。为了获得更好的控制,可以使用一个技巧:中间功能块,它显示为人工出口接口,入口流量可以通过过滤器重定向,但与网络堆栈的其余部分几乎没有交互。一旦到位,就可以在其上应用出口功能,即使其中一些可能并不总是有帮助,考虑到传入流量的真正控制权不在接收系统手中。所以在这里我设置
ifb0
界面然后在上面复制(出口) 设置,以使某种入口整形表现得比监管更好。创建ifb0 (参见注释 4)并应用与之前的egress相同的设置:
指向它们的类和过滤器:
或单个限制,而不是上面的 6 个命令:
从eth0的入口到ifb0 出口的重定向在下面完成。为了优化,只重定向目标端口而不是所有流量。无论如何,实际的过滤和整形都是在ifb0中完成的。
笔记:
1. 在 Debian 10 / 内核 5.3 上使用一些网络命名空间进行了测试。命令语法也在 CentOS 7.6 容器/内核 5.3(而不是 3.10)上进行了测试。
2.
u32 match ip sport 1081 0xffff
本来可以用来匹配源端口 1081。但它不能处理 IP 选项的存在。可以处理它,但它实际上需要三个u32过滤器u32 match tcp src 1081 0xffff
的复杂使用,如手册页中所述。所以我最终选择了。basic match
3.是否指定
ingress
了保留句柄ffff:
(指定的句柄值被忽略),所以我宁愿不指定它。引用 ingress byparent ffff:
可以替换为 justingress
so 这就是我选择的。4、第一次创建IFB接口时,会加载ifb模块,默认会在初始命名空间中自动创建ifb0和ifb1接口,如果询问接口名称ifb0会报错,而实际创建为命令的结果。同时,如果只是加载模块,这个接口不会出现在网络命名空间(例如:容器)中,所以仍然需要在那里。因此,添加
2>/dev/null || :
解决了这两种情况。当然,我假设 IFB 支持确实可用。