如何在用户空间接收 127.0.0.0/8 dst 地址的 UDP 数据包

如何在用户空间接收 127.0.0.0/8 dst 地址的 UDP 数据包

我想知道 Linux 中是否可以在用户空间接收具有 127.0.0.0/8 dst 地址但来自外部接口的 UDP 数据包。

我用 nc 测试了它,我可以看到虽然 nc 绑定到所有地址,但它没有收到数据包。

设备1我操纵本地路由表将此数据包路由到所需的接口,然后发送测试数据包。
设备1:

frr:~# ip route show table local 
...  
127.1.1.1 nhid 17  encap mpls  16 via 10.10.10.5 dev eth5
...
frr:~# echo "foo" | nc -w1 -u -v -s 3.3.3.3 127.1.1.1 3503

设备2:

frr:~# nc -l -u -p 3503

在设备 2 接口上的wireshark 中生成并捕获以下数据包: 在此输入图像描述

我知道根据RFC 1812这绝对不应该发生。另一方面,这是一个有效的用例RFC4379。这里的技巧是,我发送的数据包实际上不是 IP 路由的,而是 MPLS 交换的,并且在最后一跳,由于 PHP(倒数第二跳弹出)而丢失了 MPLS 标签,并且使用 127.0.0.0/8 地址作为 dst 的目标是确保当标签堆栈耗尽或没有有效的下一跳时,路由器不会根据 IP 地址转发,而是处理数据包。这称为 MPLS OAM 或 LSP Ping。

答案1

默认情况下Linux 内核在对具有此类源或目的地的数据包执行路由验证时强制执行 RFC 1812。以下是一些示例: IPv4:1 2 3 4 5,ARP:6。它们看起来都差不多:

  if (ipv4_is_loopback(saddr) && !IN_DEV_ROUTE_LOCALNET(in_dev))
      return -EINVAL;

如果地址的源或目标在 127.0.0.0/8 内,并且这不会发生在主机内,即通过环回接口,则数据包将被丢弃...除非专用系统控制切换route_localnet(由上面的宏检查)已启用,在这种情况下,该地址被视为正常的可路由地址:

route_localnet- 布尔值

路由时不要将环回地址视为火星源或目的地。这使得 127/8 能够用于本地路由目的。

默认为假

因此,要让接口接受或发出此类流量而不被路由堆栈丢弃,必须在其上启用切换。对于接口,eth5这可以通过以下方式完成:

sysctl -w net.ipv4.conf.eth5.route_localnet=1

根据封装的完成方式和整体设置,可能会涉及其他接口,如果有疑问,请使用all而不是首先使用。eth5


这是我在 SU 上回答的关于网络上此类数据包的问题:

将发往 127.0.0.1/8 上的地址的数据包发送到 Ubuntu 上的网络

提示:也禁用rp_filter调试时到处(包括alldefault)直到它开始工作。这就是导致一些RTNETLINK answers: Invalid argument直到全部两个方向的路由配置正确。


除了这个 RFC 4379 用例之外,一种可能更常见的情况是,完成目标 NAT 将普通地址更改为环回地址,以廉价地克服仅将其服务绑定在环回地址上的应用程序。简化示例:

iptables -t nat -I PREROUTING -p tcp --dport 80 -j DNAT --to-destination 127.0.0.1:8080

它只能在启用切换的情况下工作,因为路由堆栈本身会看到数据包不在环回上内部有环回地址(传入目的地,回复源)。线路上实际上没有出现这样的数据包(NAT 发生),但路由堆栈不知道这一点,因此必须忽略检查。

答案2

lo0创建时,它被分配为 127.0.0.1,网络掩码为 /8。这是由内核完成的(参见 参考资料net/ipv4/devinet.c)。因此,如果您想尝试重用部分 127.0.0.0/8 空间,则必须在初始化后重新分配 IP 掩码。因此,在将以太网设置为 127.1.1.0/24 之前,您可能应该将网络设置lo0为 127.0.0.0/24。

请注意,有许多进程期望 127.0.0.0/8 位于 上lo0。因此,将网络设置lo0为 /24 可能会起作用,但可能会破坏其他进程。您可能想使用无 systemd 的发行版。

目前正在讨论回收 127.0.0.0/8 的至少一部分。参见示例IETF 草案

您将无法通过标准路由器。您还需要自定义它们。

相关内容