如何为从全局网络到特殊网络命名空间的两个 UDP 连接设置无状态 NAT?

如何为从全局网络到特殊网络命名空间的两个 UDP 连接设置无状态 NAT?

需要为从全局网络命名空间中的物理网络适配器(通过一对链接的虚拟网络适配器)到在特殊网络命名空间中运行的服务的两个 UDP 连接设置无状态 NAT。这应该在运行带有内核的 Linux (Debian) 的工业设备中的 CPU (Intel Atom) 上完成5.9.7

以下是应设置的网络配置方案:

=====================    =====================================================
|| application CPU ||    ||                communication CPU                ||
||                 ||    ||                                                 ||
||                 ||    ||    global namespace    |   nsprot1 namespace    ||
||                 ||    ||                        |                        ||
||     enp4s0      ||    ||       enp1s0           |          enp3s0        ||
||    0.0.0.5/30  ==========     0.0.0.6/30        |    192.168.2.15/24    =======
||                 ||    ||                        |                        ||
|| UDP port 50001  ||    || UDP port 50001 for sv1 |  TCP port 2404 for sv2 ||
|| UDP port 50002  ||    || UDP port 50002 for sv1 |                        ||
|| UDP port 53401  ||    || UDP port 50401 for sv1 |                        ||
|| UDP port 53402  ||    || UDP port 50402 for sv1 |                        ||
||                 ||    ||                        |                        ||
||                 ||    ||      vprot0            |         vprot1         ||
||                 ||    ||     0.0.0.16/31       ---      0.0.0.17/31      ||
||                 ||    ||                        |                        ||
|| UDP port 53404  ||    || UDP port 50404 for sv2 - UDP port 50404 for sv2 ||
|| UDP port 53441  ||    || UDP port 50441 for sv2 - UDP port 50441 for sv2 ||
=====================    =====================================================

应用程序 CPU 始终首先启动,并打开多个 UDP 端口,用于通过具有 IP 地址的物理网络适配器与通信 CPU 上的服务sv1和服务进行通信。sv2enp4s00.0.0.5

在应用程序CPU上执行的输出ss --ipv4 --all --numeric --processes --udp是:

Netid  State   Recv-Q  Send-Q   Local Address:Port    Peer Address:Port   Process
udp    UNCONN  0       0              0.0.0.0:50001        0.0.0.0:*      users:(("sva",pid=471,fd=5))
udp    UNCONN  0       0              0.0.0.0:50002        0.0.0.0:*      users:(("sva",pid=471,fd=6))
udp    ESTAB   0       0              0.0.0.5:53401        0.0.0.6:50401  users:(("sva",pid=471,fd=12))
udp    ESTAB   0       0              0.0.0.5:53402        0.0.0.6:50402  users:(("sva",pid=471,fd=13))
udp    ESTAB   0       0              0.0.0.5:53404        0.0.0.6:50404  users:(("sva",pid=471,fd=19))
udp    ESTAB   0       0              0.0.0.5:53441        0.0.0.6:50441  users:(("sva",pid=471,fd=21))

通信CPU第二次启动,最后有两个服务正在运行:

  • sv1在全局命名空间中
  • sv2在特殊的网络命名空间中nsprot1

ss --ipv4 --all --numeric --processes --udp在通信CPU的全局命名空间中执行的输出是:

Netid  State   Recv-Q  Send-Q   Local Address:Port    Peer Address:Port   Process
udp    UNCONN  0       0              0.0.0.0:50001        0.0.0.0:*      users:(("sv1",pid=812,fd=18))
udp    UNCONN  0       0              0.0.0.6:50002        0.0.0.0:*      users:(("sv1",pid=812,fd=17))
udp    UNCONN  0       0              0.0.0.6:50401        0.0.0.0:*      users:(("sv1",pid=812,fd=13))
udp    UNCONN  0       0              0.0.0.6:50402        0.0.0.0:*      users:(("sv1",pid=812,fd=15))

ip netns exec nsprot1 ss --ipv4 --all --numeric --processes --udp(命名空间)的输出nsprot1是:

Netid  State   Recv-Q  Send-Q   Local Address:Port    Peer Address:Port   Process
udp    ESTAB   0       0             0.0.0.17:50404        0.0.0.5:53404  users:(("sv2",pid=2421,fd=11))
udp    ESTAB   0       0             0.0.0.17:50441        0.0.0.5:53441  users:(("sv2",pid=2421,fd=12))

sysctl一般情况下,所有物理网络适配器都会启用 IPv4 转发。
仅广播和组播转发因不需要且不想要而被禁用。

使用以下命令在通信 CPU 上设置网络配置:

ip netns add nsprot1
ip link add vprot0 type veth peer name vprot1 netns nsprot1
ip link set dev enp3s0 netns nsprot1
ip address add 0.0.0.16/31 dev vprot0
ip netns exec nsprot1 ip address add 0.0.0.17/31 dev vprot1
ip netns exec nsprot1 ip address add 192.168.2.15/24 dev enp3s0
ip link set dev vprot0 up
ip netns exec nsprot1 ip link set vprot1 up
ip netns exec nsprot1 ip link set enp3s0 up
ip netns exec nsprot1 ip route add 0.0.0.4/30 via 0.0.0.16 dev vprot1

使用以下命令设置网络地址转换:

nft add table ip prot1
nft add chain ip prot1 prerouting '{ type nat hook prerouting priority -100; policy accept; }'
nft add rule prot1 prerouting iif enp1s0 udp dport '{ 50404, 50441 }' dnat 0.0.0.17
nft add chain ip prot1 postrouting '{ type nat hook postrouting priority 100; policy accept; }'
nft add rule prot1 postrouting ip saddr 0.0.0.16/31 oif enp1s0 snat 0.0.0.6

的输出nft list table ip prot1是:

table ip prot1 {
    chain prerouting {
        type nat hook prerouting priority -100; policy accept;
        iif "enp1s0" udp dport { 50404, 50441 } dnat to 0.0.0.17
    }

    chain postrouting {
        type nat hook postrouting priority 100; policy accept;
        ip saddr 0.0.0.16/31 oif "enp1s0" snat to 0.0.0.6
    }
}

在全局命名空间中另外定义了仅有的inet filter中包含:

table inet filter {
    chain input {
        type filter hook input priority 0; policy accept;
    }

    chain forward {
        type filter hook forward priority 0; policy accept;
    }

    chain output {
        type filter hook output priority 0; policy accept;
    }
}

该 NAT 配置适用于有状态 NAT。它适用于具有端口号的 UDP 通道50404 ,并且53404由于sv2最后启动而打开0.0.0.17:50404并向其发送 UDP 数据包, 该数据包在全局命名空间的钩子0.0.0.5:53404中应用了源网络地址转换。应用CPU的服务发回一个UDP数据包,从到。 UDP 数据包未通过to 的规则。正如我后来发现的那样,它是通过连接跟踪直接发送的。postroutingenp1s0sva0.0.0.5:534040.0.0.6:504040.0.0.17:50404preroutingdnat0.0.0.170.0.0.17

但此状态 NAT 配置不适用于端口号为50441和 的UDP 通道534441。看起来原因是sva应用程序 CPU 在服务启动之前就已经发送了多个 UDP 数据包,并且目标端口在网络命名空间中0.0.0.5:53441打开。 ICMP 返回目标端口不可达。考虑到目的地港口尚未开放,这并不奇怪。遗憾的是,在服务启动并打开两个 UDP 端口之前,无法阻止服务中的 UDP 数据包发送。服务定期发送,有时还会额外触发自发的 UDP 数据包,与连接状态无关。0.0.0.6:50441sv2nsprot1svasv2sva0.0.0.5:534410.0.0.6:50441

因此,此配置的问题似乎是有状态 NAT,因为钩子dnat中的规则prerouting仍未在最终在网络命名空间中打开的目标端口上使用nsprot1。仍然继续路由 UDP 数据包,0.0.0.6:50441导致丢弃 UDP 数据包并返回 ICMP,表明目标端口不可到达。

因此,解决方案可能是使用无状态 NAT。因此还执行了以下命令:

nft add table ip raw
nft add chain ip raw prerouting '{ type filter hook prerouting priority -300; policy accept; }'
nft add rule ip raw prerouting udp dport '{ 50404, 50441, 53404, 53441 }' notrack

但结果并不如预期。对于来自输入接口和目标端口的UDP 数据包prerouting,将目标地址从 更改为 的规则0.0.0.6仍然没有考虑 。0.0.0.17enp1s05040450441

接下来是我执行的:

nft add table ip filter
nft add chain filter trace_in '{ type filter hook prerouting priority -301; }'
nft add rule filter trace_in meta nftrace set 1
nft add chain filter trace_out '{ type filter hook postrouting priority 99; }'
nft add rule filter trace_out meta nftrace set 1
nft monitor trace

我查看了跟踪,可以看到notrack规则已被考虑在内,但随后带有目标端口的 UDP 数据包50441会直接传递到input挂钩。我不知道为什么。

我非常仔细地研究了很多很多个小时,如下:

我尝试了很多不同的配置,使用了 Wireshark,使用过nft monitor trace,但我找不到适用于带有端口的 UDP 通道50441以及53441在目标端口打开sva之前发送 UDP 数据包的解决方案。0.0.0.17:50441

如果我在应用程序 CPU 上手动终止服务sva,在通信 CPU 上设置网络配置并启动这两个服务sv1sv2最后sva在通信 CPU 上已打开的所有 UDP 端口上再次手动启动该服务,则有状态 NAT 配置将起作用。但默认情况下,工业设备中无法执行此启动服务的顺序。应用程序服务sva必须独立于通信服务是否准备好进行通信而运行。

哪些命令(链/规则)对于两个 UDP 通道具有无状态 NAT 是必需的0.0.0.5:53404 - 0.0.0.17:50404,并且0.0.0.5:53441 - 0.0.0.17:50441独立于目标端口的打开状态,以及哪个服务首先向另一个服务发送 UDP 数据包?

PS:该服务sv2可以根据设备的配置启动,也可以在全局命名空间中使用不同的物理网络适配器,不需要 NAT 和网络命名空间。在这个网络配置下,三个服务之间的UDP通信绝对没有问题。

答案1

经过很多很多个小时的阅读文档、教程、各种网页上的建议、进行大量的尝试以及进行深入而全面的网络和 netfilter 监控和分析,我终于自己找到了解决方案。

nft add table ip prot1
nft add chain ip prot1 prerouting '{ type filter hook prerouting priority -300; policy accept; }'
nft add rule ip prot1 prerouting iif enp1s0 udp dport '{ 50404, 50441 }' ip daddr set 0.0.0.17 notrack accept
nft add rule ip prot1 prerouting iif vprot0 ip saddr 0.0.0.17 notrack accept
nft add chain ip prot1 postrouting '{ type filter hook postrouting priority 100; policy accept; }'
nft add rule ip prot1 postrouting oif enp1s0 ip saddr 0.0.0.17 ip saddr set 0.0.0.6 accept

网络过滤钩子应首先打开并阅读页面以理解以下说明。

使用命令说明:

  1. A网络过滤器 桌子ip为名称为 的协议 (IPv4)添加prot1
  2. A被添加到具有优先级的钩子类型prot1名称的表中。使用低于的优先级编号非常重要,这样才能绕过连接跟踪。这排除了使用具有更低优先级的目标网络地址转换类型链。preroutingfilterprerouting-300-200conntracknat
  3. A筛选 规则添加到表链prot1prerouting仅适用于协议类型为或 的输入接口ii接收的 IPv4 数据包,将数据包的目标从修改为并激活此UDP 数据包的连接。尽管实际上没有必要将从应用程序CPU的服务接收到的用于通信CPU的服务的UDP数据包尽可能快地传递到下一个钩子(在本例中是钩子),但是明确地指定了判决。fenp1s0udpdport5040450441ip daddr0.0.0.60.0.0.17no trackacceptsvasv2forward
  4. 一秒筛选 规则添加到表prot1到链prerouting,仅应用于全部i在输入接口上接收的 IPv4 数据包与协议类型(、、...)无关,具有i激活该数据包连接的来源。当然也可以仅过滤具有适当源或目标端口号的 UDP 数据包,但此处不需要此附加限制,并且此规则也适用于从尚未打开的目标端口发送回的 ICMP 数据包,因为该服务目前没有运行。再次显式指定判决,而不是使用隐式默认值将数据包尽快传递到钩子。fvprot0udpicmpip saddr0.0.0.17no track0.0.0.170.0.0.5sv2acceptcontinueforward
  5. 一秒被添加到具有优先级的钩子类型prot1名称的表中。重要的是使用类型链而不是类型链,以便能够对绕过连接跟踪的 UDP(和 ICMP)数据包应用源地址转换。postroutingfilterpostrouting100filternat
  6. A筛选 规则添加到prot1第二条链的表中postrouting,该链仅适用于在o输出接口上发送的 IPv4 数据包i,与协议类型(、、 ...)无关,具有将数据包的来源从修改为 的源ess 。尽管实际上并不需要尽快将从通信CPU的服务接收到的UDP数据包传递到应用CPU的服务,但是再次明确地指定该判决。此规则还将由于服务尚未运行而无法到达的目标端口发送的 ICMP 数据包的源地址更改为。因此,应用程序 CPU 永远不会注意到它使用不同接口进行两个 UDP 通道的通信,这是要满足的第二个要求,尽管这并不重要。fenp1s0udpicmpsaddr0.0.0.17ip saddr0.0.0.170.0.0.6acceptsv2sva0.0.0.60.0.0.17sv20.0.0.6

发现这种非常特殊的网络配置和服务之间的通信类型需要无状态网络转换sva并且sv2必须完成 NAT是一项艰巨的工作没有使用nat钩子。

答案2

非常感谢你的文章。对我帮助很大。

还有一个提示:如果您正在进行设置但它不起作用,请使用“conntrack -L”检查 contrack 中的现有连接,并使用“conntrack -D”删除它们。

这是因为我花了几个小时在我的配置中搜索探针 - 没有问题,但过去的连接仍然可用。

相关内容