这是一个专门关于nftables 链类型在 Linux 内核中。
我不明白它们是如何处理的。我已经盯着内核代码一段时间了,在我看来,nftables“链”作为钩子条目附加到 netns(例如,struct netns_nf.hooks_ipv4
对于 IPv4)。
在创建或处理链时,我没有看到任何对链的“类型”有任何歧视—— filter
、nat
、 或。route
看起来所有链类型都会简单地作为钩子条目填充,并且只有函数struct nf_hook_entry.hook
是特定于类型的。例如,我认为这将是一个链的nf_hook_entry.hook
功能。nft_nat_do_chain
type nat
看着该表包含族、钩子和类型的组合,假设我在input
钩子上添加了两条链,一条带有type filter
,一条带有type nat
。进一步说,两条链都是以相同的优先级创建的。
问题:
- 我的假设场景是否可能,同一个钩子上有两条链,仅因类型而异?如果不是,内核在哪里阻止这种情况发生?
- 如果可能的话,什么决定这两条链的运行顺序?我是否缺少一些运行的东西,例如
type nat
before 链type filter
?或者它会取决于第一个和第二个添加的链(也许还有内核版本等)?
有一个关于具有相同优先级的链的优秀相关答案,但具体情况是有两条链同类型的。
我问这个问题的最终目的是为了理解为什么 nftables 有“类型”的概念。
例如,我知道type route
链的处理程序可能会调用ip_route_me_harder
(不是开玩笑!) 如果数据包的某些字段被链更改,这是 的链所独有的type route
。我知道type nat
它的优先级有一些限制。我还读到type nat
链条是仅有的调用连接的第一个数据包,但我无法在代码中的任何位置找到该确切的限制(尽管可能在nf_nat_inet_fn
?nf_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_DEFRAG
或NF_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
. pn1
,pn3
并且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 挂钩都发生在 和 之间pf2
,pf3
即优先级 -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
。