这条指令听起来很完整:把每个 VPN 客户端限制到 20 Mbit/s。但它并不完整,隧道接口上的单一队列规则并不能提供它。「每个客户端」可能指每个隧道地址、每台设备或每个账号,而每种选择对数据包的分类都不同。写任何规则之前,先确定需求真正指的是哪一种。

下文全部讨论的就是这句话与能用的东西之间的距离。

「每个客户端」首先必须意味着什么

隧道地址容易分类,也容易回收。设备则更难,因为一个账号可能带着多台设备,而一台设备也可能持有多个地址,尤其是 IPv6。对公平策略而言,账号是最有意义的单位,也是在数据包头部中最难识别的。每种定义都隐含自己的计数键,因此在它们之间摇摆的策略不会做到描述所称的事。

方向就是第二个缺失的词。上传、下载与合计是三种不同的需求,而单个接口上的一条出站规则很少能同时覆盖两条路径。

选一个客户端仍然可见的点

每个方向都是相对于某个接口定义的。离开服务器隧道、朝向远端客户端的东西,对该客户端而言算作下载;客户端发出的东西进入隧道,然后朝其目的地出去。为每个方向选一个调度点,并问那里存在哪些字段。在源 NAT 之前,上传仍然带着客户端的内网地址;经过转换之后,单纯按源地址匹配可能就再也认不出它了。

在加密路径上,头部属于 VPN 传输层,因此匹配内网地址的过滤器没有可匹配的东西。设计可以先分类,再把标记交给更晚的调度器,但这次交接必须是有意为之:包标记、连接标记与调度器类别是不同的状态,而不是一个被一路传递的值。

桶不会自己出现

挂在接口上的令牌桶只塑造使用它的任何流量;它并不会为每个源地址新铸一个桶。基于类的设计则把选定的流量归入各类,由过滤器决定成员资格,而 HTB 手册在父类有余量带宽时会把类的速率与上限分开。如果意图是硬上限,就设置上限并有意识地决定借用行为;不要把每个速率数字都当成绝对最大值。

当需求是按客户端时,同时进行的两条传输应当共享同一份额度。给每条流全额速率是另一种策略,而任何只观察一条下载的测试都能让它过关。

两台设备、一个账号、一份额度?

分别限制两个设备地址,永远不会累加成一个账号级的额度,即便两台设备以同一个人登录。整段被转换到一个对端之后的 LAN,以及分类器从未预料到的额外 IPv6 地址,也是同样的道理。让地址与身份的对应保持最新,并记住稳定地址只是简化了映射,并不等于验证了其所有者。

还要定义突发与队列行为。令牌桶允许高于平均值的短暂突发,而缓冲会增加时延;对超量部分做监管,和把它排队留到以后,是两个结果不同的决定。

验证时,从一个客户端发起两条传输,再加入第二个客户端,并读取每个方向的类计数器。

相关文章