nftables:多种类型的链是否都针对给定的钩子进行评估?

nftables:多种类型的链是否都针对给定的钩子进行评估?

这是一个专门关于nftables 链类型在 Linux 内核中。

我不明白它们是如何处理的。我已经盯着内核代码一段时间了,在我看来,nftables“链”作为钩子条目附加到 netns(例如,struct netns_nf.hooks_ipv4对于 IPv4)。

在创建或处理链时,我没有看到任何对链的“类型”有任何歧视—— filternat、 或。route看起来所有链类型都会简单地作为钩子条目填充,并且只有函数struct nf_hook_entry.hook是特定于类型的。例如,我认为这将是一个链的nf_hook_entry.hook功能。nft_nat_do_chaintype nat

看着该表包含族、钩子和类型的组合,假设我在input钩子上添加了两条链,一条带有type filter,一条带有type nat。进一步说,两条链都是以相同的优先级创建的。

问题:

  1. 我的假设场景是否可能,同一个钩子上有两条链,仅因类型而异?如果不是,内核在哪里阻止这种情况发生?
  2. 如果可能的话,什么决定这两条链的运行顺序?我是否缺少一些运行的东西,例如type natbefore 链type filter?或者它会取决于第一个和第二个添加的链(也许还有内核版本等)?

一个关于具有相同优先级的链的优秀相关答案,但具体情况是有两条链同类型的

我问这个问题的最终目的是为了理解为什么 nftables 有“类型”的概念。

例如,我知道type route链的处理程序可能会调用ip_route_me_harder不是开玩笑!) 如果数据包的某些字段被链更改,这是 的链所独有的type route。我知道type nat它的优先级有一些限制。我还读到type nat链条是仅有的调用连接的第一个数据包,但我无法在代码中的任何位置找到该确切的限制(尽管可能在nf_nat_inet_fnnf_nat_core.c)。

我很感激您能给我的任何指示,帮助我了解type内核中 nftables 链的处理方式和位置!

编辑: 这个答案似乎表明 nftables“类型”几乎是一种风格选择,尽管它确实指出了该类型的特殊行为route。另一个答案更加混乱我的Waters 说 NAT 规则不能添加到 的链中type filter,这(如果是真的)让我感到非常困惑。这样的限制是在哪里实施的? (仅在用户空间?)

答案1

长话短说

当进行网络命名空间接收流量并对其进行 NAT 的实验时,我们可以看到,无论赋予链的优先级如何type nat hook prerouting,过滤器链的优先级都无关紧要:NAT 总是以精确的预路由钩子优先级发生 - 100 又名NF_IP_PRI_NAT_DST. NAT 链本身之间的优先级被保留。

您查看了.hook定义中用于数据包遍历期间实际操作的条目,但忽略了仅为 NAT 挂钩定义的.ops_register/.ops_unregister条目,这些条目在注册链时引入了不同的行为。

使用内核 6.5.x 和nftables1.0.9,提供了一些链接https://elixir.bootlin.com/目前使用最新的 LTS 内核,没有补丁版本:6.1(不是 6.1.x)。

总结一下:

  • NAT 以特殊的钩子优先级起作用,并且在与其他钩子类型(例如筛选或者路线:NAT 链的注册方式与其他链不同。给定的优先级仍然适用于连接在同一位置的不同 NAT 链之间。

  • 路线遵循正常的优先级,就像筛选(无需特殊注册)。

  • 不要NF_IP_PRI_NAT_DST在其他地方使用精确的优先级,例如(或各种其他与 NAT 相关的精确值),因为这样的话,如何精确地交互nftablesNetfilter 中的 NAT 挂钩可能是未定义的(例如:可能会根据创建顺序而变化,或者行为可能会根据内核版本而变化),而不是确定性的。例如,在 DNAT 之前使用 -101 或更少,或者在 DNAT 之后使用 -99 或更多,但永远不要使用 -100 以避免未定义的行为。

  • 同样的警告适用于其他特殊设施的优先事项,例如那里,例如NF_IP_PRI_CONNTRACK_DEFRAGNF_IP_PRI_CONNTRACK等等(并且对于iptables交互时的优先事项iptables规则并需要确定性结果)。


实验

我把诸如家庭之类的情况放在一边inet:人们可以通过足够的规则集和测试用例来检查它的行为是否相同。

规则集示例(使用 加载nft -f ...):

table t         # for idempotence
delete table t  # for idempotence

table t {
    chain pf1 {
        type filter hook prerouting priority -250; policy accept;

    udp dport 5555 meta nftrace set 1 counter
    }

    chain pf2 {
        type filter hook prerouting priority -101; policy accept;

    udp dport 5555 counter accept
    udp dport 6666 counter accept
    }

    chain pf3 {
        type filter hook prerouting priority -99; policy accept;

    udp dport 5555 counter accept
    udp dport 6666 counter accept
    }

    chain pn1 {
        type nat hook prerouting priority -160; policy accept;

        counter
    }

    chain pn2 {
        type nat hook prerouting priority 180; policy accept;

        udp dport 5555 counter dnat to :6666
    }

    chain pn3 {
        type nat hook prerouting priority -190; policy accept;

        counter
    }

    chain pn4 {
        type nat hook prerouting priority 190; policy accept;

        udp dport 5555 counter dnat to :7777
        udp dport 6666 counter dnat to :7777
    }

}

此规则集会将收到的 UDP 端口 5555 更改为端口 6666,而不是在pn2. pn1pn3并且pn4在这里只是为了 NAT 链之间的优先级(pn4也在这里解释给定类型的 NAT(DNAT、SNAT...)仅发生一次)。 UDP 端口 6666 上有一个接收应用程序(因此该流不会被无法到达的 ICMP 目标端口删除),我socat UDP4-LISTEN:6666,fork EXEC:date用于此测试并(交互地)从偏僻的客户端使用socat UDP4:192.0.2.2:5555 -.

人们会认为pn2优先级为 180 的 NAT 链会执行 DNAT优先级为-99的过滤器链pf3。但这并不是type nat其他类型之间发生的情况:NAT 很特殊。使用nft monitor trace如下:

# nft monitor trace
trace id 4ab9ba62 ip t pf1 packet: iif "lan0" ether saddr 8e:3e:82:1a:dc:87 ether daddr fa:2f:7e:2d:f1:03 ip saddr 192.0.2.1 ip daddr 192.0.2.2 ip dscp cs0 ip ecn not-ect ip ttl 64 ip id 49393 ip length 30 udp sport 58201 udp dport 5555 udp length 10 @th,64,16 0x610a
trace id 4ab9ba62 ip t pf1 rule udp dport 5555 meta nftrace set 1 counter packets 0 bytes 0 (verdict continue)
trace id 4ab9ba62 ip t pf1 verdict continue
trace id 4ab9ba62 ip t pf1 policy accept
trace id 4ab9ba62 ip t pf2 packet: iif "lan0" ether saddr 8e:3e:82:1a:dc:87 ether daddr fa:2f:7e:2d:f1:03 ip saddr 192.0.2.1 ip daddr 192.0.2.2 ip dscp cs0 ip ecn not-ect ip ttl 64 ip id 49393 ip length 30 udp sport 58201 udp dport 5555 udp length 10 @th,64,16 0x610a
trace id 4ab9ba62 ip t pf2 rule udp dport 5555 counter packets 0 bytes 0 accept (verdict accept)
trace id 4ab9ba62 ip t pn3 packet: iif "lan0" ether saddr 8e:3e:82:1a:dc:87 ether daddr fa:2f:7e:2d:f1:03 ip saddr 192.0.2.1 ip daddr 192.0.2.2 ip dscp cs0 ip ecn not-ect ip ttl 64 ip id 49393 ip length 30 udp sport 58201 udp dport 5555 udp length 10 @th,64,16 0x610a
trace id 4ab9ba62 ip t pn3 rule counter packets 0 bytes 0 (verdict continue)
trace id 4ab9ba62 ip t pn3 verdict continue
trace id 4ab9ba62 ip t pn3 policy accept
trace id 4ab9ba62 ip t pn1 packet: iif "lan0" ether saddr 8e:3e:82:1a:dc:87 ether daddr fa:2f:7e:2d:f1:03 ip saddr 192.0.2.1 ip daddr 192.0.2.2 ip dscp cs0 ip ecn not-ect ip ttl 64 ip id 49393 ip length 30 udp sport 58201 udp dport 5555 udp length 10 @th,64,16 0x610a
trace id 4ab9ba62 ip t pn1 rule counter packets 0 bytes 0 (verdict continue)
trace id 4ab9ba62 ip t pn1 verdict continue
trace id 4ab9ba62 ip t pn1 policy accept
trace id 4ab9ba62 ip t pn2 packet: iif "lan0" ether saddr 8e:3e:82:1a:dc:87 ether daddr fa:2f:7e:2d:f1:03 ip saddr 192.0.2.1 ip daddr 192.0.2.2 ip dscp cs0 ip ecn not-ect ip ttl 64 ip id 49393 ip length 30 udp sport 58201 udp dport 5555 udp length 10 @th,64,16 0x610a
trace id 4ab9ba62 ip t pn2 rule udp dport 5555 counter packets 0 bytes 0 dnat to :6666 (verdict accept)
trace id 4ab9ba62 ip t pf3 packet: iif "lan0" ether saddr 8e:3e:82:1a:dc:87 ether daddr fa:2f:7e:2d:f1:03 ip saddr 192.0.2.1 ip daddr 192.0.2.2 ip dscp cs0 ip ecn not-ect ip ttl 64 ip id 49393 ip length 30 udp sport 58201 udp dport 6666 udp length 10 @th,64,16 0x610a
trace id 4ab9ba62 ip t pf3 rule udp dport 6666 counter packets 0 bytes 0 accept (verdict accept)

trace id 46ad0497 ip t pf1 packet: iif "lan0" ether saddr 8e:3e:82:1a:dc:87 ether daddr fa:2f:7e:2d:f1:03 ip saddr 192.0.2.1 ip daddr 192.0.2.2 ip dscp cs0 ip ecn not-ect ip ttl 64 ip id 49394 ip length 30 udp sport 58201 udp dport 5555 udp length 10 @th,64,16 0x620a
trace id 46ad0497 ip t pf1 rule udp dport 5555 meta nftrace set 1 counter packets 0 bytes 0 (verdict continue)
trace id 46ad0497 ip t pf1 verdict continue
trace id 46ad0497 ip t pf1 policy accept
trace id 46ad0497 ip t pf2 packet: iif "lan0" ether saddr 8e:3e:82:1a:dc:87 ether daddr fa:2f:7e:2d:f1:03 ip saddr 192.0.2.1 ip daddr 192.0.2.2 ip dscp cs0 ip ecn not-ect ip ttl 64 ip id 49394 ip length 30 udp sport 58201 udp dport 5555 udp length 10 @th,64,16 0x620a
trace id 46ad0497 ip t pf2 rule udp dport 5555 counter packets 0 bytes 0 accept (verdict accept)
trace id 46ad0497 ip t pf3 packet: iif "lan0" ether saddr 8e:3e:82:1a:dc:87 ether daddr fa:2f:7e:2d:f1:03 ip saddr 192.0.2.1 ip daddr 192.0.2.2 ip dscp cs0 ip ecn not-ect ip ttl 64 ip id 49394 ip length 30 udp sport 58201 udp dport 6666 udp length 10 @th,64,16 0x620a
trace id 46ad0497 ip t pf3 rule udp dport 6666 counter packets 0 bytes 0 accept (verdict accept)
^C

可以看到所有预路由 NAT 挂钩都发生在 和 之间pf2pf3即优先级 -101 和 -99 之间:在优先级 -100 处,即NF_IP_PRI_NAT_DST用于 Netfilter 自己的结构static const struct nf_hook_ops nf_nat_ipv4_ops[]。链ip t pf3看到端口 6666 而不是 5555。

如果已应用 NAT 语句,则 Netfilter 会跳过以下规则(在同一挂钩中),因此pn4在上面的示例中根本没有机会遍历这里(最初只有 2 个相同流的数据包到达端口 5555)并且永远不会出现:此行为也与type filter仍遍历下一个钩子的位置不同(例如:pf3在 后仍遍历pf2)。

与往常一样,流中的下一个数据包不再触发任何 NAT 链,因为只有创建新流(conntrack 状态 NEW)的数据包才会发送到 NAT 链,因此下一个数据包甚至不再显示遍历pnX链。四个预路由 NAT 链之间的优先级受到尊重:优先级顺序是pn3(-190) 、pn1(-160) 、pn2(180) (然后会有pn4(190) 但没有机会)。

注意:事实上,数据包/字节计数器在同一运行中没有出现增加,对nft monitor trace我来说看起来像是一个错误或缺少的功能(在检查时它们会增加nft list ruleset)。

type nat挂钩使用与其他默认功能不同的注册功能nftables钩子因此可以以不同的方式处理它们:

.ops_register = nf_nat_ipv4_register_fn,
.ops_unregister = nf_nat_ipv4_unregister_fn,

它由 NAT(由 Netfilter 管理)处理并挂接NF_INET_PRE_ROUTING(仍然由 Netfilter 提供nftables)这将优先完成NF_IP_PRI_NAT_DST

这不是为了类型过滤器(也不路线)然后将使用一个通用的nftables方法而不是指定的一个

相关内容