12.4. 用于极快速大规模过滤的哈希过滤器

如果您需要数千条规则,例如,如果您有很多客户端或计算机,并且它们都具有不同的 QoS 规范,您可能会发现内核花费大量时间来匹配所有这些规则。

默认情况下,所有过滤器都驻留在一个大的链中,该链按照优先级降序进行匹配。如果您有 1000 条规则,则可能需要 1000 次检查才能确定如何处理数据包。

如果您有 256 个链,每个链包含四个规则,匹配速度会快得多 - 如果您可以将数据包分配到这 256 个链中,以便正确的规则在那里。

哈希使这成为可能。假设您的网络中有 1024 个有线调制解调器客户,其 IP 地址范围从 1.2.0.0 到 1.2.3.255,并且每个客户都必须进入另一个类别,例如“lite”、“regular”和“premium”。那么您将拥有 1024 条这样的规则

# tc filter add dev eth1 parent 1:0 protocol ip prio 100 match ip src \
  1.2.0.0 classid 1:1
# tc filter add dev eth1 parent 1:0 protocol ip prio 100 match ip src \
  1.2.0.1 classid 1:1
...
# tc filter add dev eth1 parent 1:0 protocol ip prio 100 match ip src \
  1.2.3.254 classid 1:3
# tc filter add dev eth1 parent 1:0 protocol ip prio 100 match ip src \
  1.2.3.255 classid 1:2

为了加速此过程,我们可以使用 IP 地址的最后一部分作为“哈希键”。然后我们得到 256 个表,第一个表如下所示
# tc filter add dev eth1 parent 1:0 protocol ip prio 100 match ip src \
  1.2.0.0 classid 1:1
# tc filter add dev eth1 parent 1:0 protocol ip prio 100 match ip src \
  1.2.1.0 classid 1:1
# tc filter add dev eth1 parent 1:0 protocol ip prio 100 match ip src \
  1.2.2.0 classid 1:3
# tc filter add dev eth1 parent 1:0 protocol ip prio 100 match ip src \
  1.2.3.0 classid 1:2

下一个表的开头如下所示
# tc filter add dev eth1 parent 1:0 protocol ip prio 100 match ip src \
  1.2.0.1 classid 1:1
...

这样,最多只需要四次检查,平均两次。

配置非常复杂,但是当您拥有如此多规则时,它非常值得。首先我们创建一个过滤器根,然后我们创建一个包含 256 个条目的表
# tc filter add dev eth1 parent 1:0 prio 5 protocol ip u32
# tc filter add dev eth1 parent 1:0 prio 5 handle 2: protocol ip u32 divisor 256

现在我们向创建的表中的条目添加一些规则

# tc filter add dev eth1 protocol ip parent 1:0 prio 5 u32 ht 2:7b: \
        match ip src 1.2.0.123 flowid 1:1
# tc filter add dev eth1 protocol ip parent 1:0 prio 5 u32 ht 2:7b: \
        match ip src 1.2.1.123 flowid 1:2
# tc filter add dev eth1 protocol ip parent 1:0 prio 5 u32 ht 2:7b: \
        match ip src 1.2.3.123 flowid 1:3
# tc filter add dev eth1 protocol ip parent 1:0 prio 5 u32 ht 2:7b: \
        match ip src 1.2.4.123 flowid 1:2
这是条目 123,其中包含 1.2.0.123、1.2.1.123、1.2.2.123、1.2.3.123 的匹配项,并将它们分别发送到 1:1、1:2、1:3 和 1:2。请注意,我们需要以十六进制指定我们的哈希桶,0x7b 是 123。

接下来创建一个“哈希过滤器”,将流量定向到哈希表中的正确条目
# tc filter add dev eth1 protocol ip parent 1:0 prio 5 u32 ht 800:: \
        match ip src 1.2.0.0/16 \
        hashkey mask 0x000000ff at 12 \
        link 2:
好的,有些数字需要解释。默认哈希表称为 800::,所有过滤都从那里开始。然后我们选择源地址,它位于 IP 标头中的位置 12、13、14 和 15,并指示我们只对最后一部分感兴趣。我们将其发送到我们之前创建的哈希表 2:。

这非常复杂,但它在实践中确实有效,并且性能将是惊人的。请注意,可以对本示例进行改进,使其达到每个链包含 1 个过滤器的理想情况!