如果不使用原始套接字,Linux 上是否有办法强制网络数据包首先物理发送到网关,即使目标地址是绑定到本地接口的地址?
我的系统有一个活动接口,eno1
,它被分配了一个 IPv4 地址,192.168.1.1
掩码为/20
。网关地址为192.168.0.1
。我尝试使用 来添加路由ip route add 192.168.1.0/24 via 192.168.0.1
,它对 具有最高的路由特异性192.168.1.1
。但调用traceroute 192.168.1.1
仍然只显示单跳,表明它没有到达任何地方。我希望它显示第一跳为192.168.0.1
,然后是192.168.1.1
。未遵循路由的其他证据是,发送数据包SO_TIMESTAMPING
会导致缺少硬件时间戳,这表明数据包实际上没有放到线路上。
如果这是 XY 问题,我正在尝试构建一个类似 ping 的测量工具,用于测量往返延迟和到我 ISP 的 CMTS 的数据包丢失,但那里的路由器对 ICMP 响应使用速率限制,因此我无法按我想要的频率测量延迟。此外,来自我网络上的本地设备的 ping 会违反速率限制,导致虚假丢包(尽管我想我可以阻止转发以 CMTS 为目的地或 TTL=1 的数据包)。请注意,虽然我是在我的私有网络上的系统上使用我自己的路由器作为第一跳进行本地测试,但实际上我将在路由器本身上运行它,.1.1
用我的公共 IPv4 地址替换地址,.0.1
用 CMTS 的上游路由器地址替换地址。
我的想法是向上游路由器发送一个数据包,其目的地是我自己的公共 IP,希望它能被直接发送回我自己的路由器,经过每一段路径一次,但在 ISP 看来却是通用转发流量,而不是 CMTS 直接响应的东西。
理想情况下,该解决方案可以作为超级用户配置一次,然后由用户空间作业使用,即不需要以超级用户身份运行该工具(因此希望避免使用原始套接字)。
答案1
如果不使用原始套接字,Linux 上是否有办法强制网络数据包首先物理发送到网关,即使目标地址是绑定到本地接口的地址?
创建单独的网络命名空间:
ip netns add foo
在物理以太网接口上创建一个虚拟以太网接口,然后将其移动到 netns 中:
ip link add eno1.foo link eno1 type macvlan mode private
ip link set eno1.foo netns foo
配置您的 IP 地址:
ip netns exec foo ip link set eno1.foo up
ip netns exec foo ip addr add 192.168.1.7/24 eno1.foo
运行程序:
ip netns exec foo ~/pingthing -i eno1.foo
(实际上不需要“持久”命名空间 - 您可以使用unshare --net <program>
,并通过 PID 将虚拟接口移动到其中 - 但这不太方便。)
我尝试使用 ip route add 192.168.1.0/24 via 192.168.0.1 添加路由,该路由对 192.168.1.1 具有最高的路由特异性。但调用 traceroute 192.168.1.1 仍然只显示单跳,表明它没有到达任何地方。
在路由之前,所有数据包都会根据 中设置的规则进行检查ip -4 rule
。默认情况下,第一条规则首先通过特殊的“本地”路由表(其中包含所有“本地” IP 地址的环回路由)发送它们。
仅当“本地”表中没有匹配项时,才会使用“主”路由表中的路由。
理想情况下,该解决方案可以作为超级用户配置一次,然后由用户空间作业使用,即不需要以超级用户身份运行该工具(因此希望避免使用原始套接字)。
setcap cap_net_raw=ep
如果它是 CLI 工具,则仅授予该工具 CAP_NET_RAW 权限;AmbientCapabilities=
如果它是服务,则使用 systemd 的权限。然后,您可以使用原始套接字,而无需获得任何其他 root 权限。
前者(可执行文件上的 setcap)经常被ping
和mtr
工具使用,所以这实际上并不是什么新鲜事。
(嵌入式文件系统可能不支持文件级“功能”,但在这种情况下,你仍然可以设置 setuid 位。它需要更多地关注安全性,因为你的工具做即使由普通用户运行时也能获得完全的 root 权限。)
setcap/suid 仅适用于编译后的二进制可执行文件,而不适用于解释后的脚本可执行文件。