当目标接口为本地时强制 Ping 到出口(Debian)

当目标接口为本地时强制 Ping 到出口(Debian)

我在 Proxmox 4.4 下运行基于 Debian 的 Linux 容器。此主机有五个网络接口(尽管我遇到的问题中只有两个起作用)。

当我进入该主机时,我 ping 了与 eth1 关联的 IP 地址。实际发生的情况和我认为应该发生的情况是两件截然不同的事情。

我希望 ping 数据包从 eth3 传出,然后被路由到 eth1。

实际情况是,IP 堆栈看到我正在 ping 本地接口,然后它立即将回复发送回堆栈。我知道数据包不会出去并回来,原因有两个:

  1. 数据包捕获显示没有任何东西到达 eth1 或 eth3。
  2. ping 延迟平均为 0.013 毫秒。如果数据包按预期发送和返回,延迟约为 60 毫秒。

当然,我希望在 ping 与 eth3 关联的 IP 地址时有相应的行为。在这种情况下,我希望数据包从 eth1 传出,然后路由到 eth3。不幸的是,发生了与上述类似的行为。

下面,我展示了我为尝试实现所需行为而设置的静态路由。此类路由在 Windows 计算机上按预期工作,但在我使用的 Linux 设置下不起作用。

我该如何配置该主机以便按预期转发?

root@my-host:~# uname -a
Linux my-host 4.4.35-1-pve #1 SMP Fri Dec 9 11:09:55 CET 2016 x86_64 GNU/Linux
root@my-host:~#
root@my-host:~# cat /etc/debian_version
8.9
root@my-host:~#
root@my-host:~# ifconfig
eth0      Link encap:Ethernet  HWaddr xx:xx:xx:xx:xx:xx
          inet addr:192.0.2.65  Bcast:192.0.2.255  Mask:255.255.255.0
          UP BROADCAST RUNNING MULTICAST  MTU:1500  Metric:1
          RX packets:195028 errors:0 dropped:0 overruns:0 frame:0
          TX packets:12891 errors:0 dropped:0 overruns:0 carrier:0
          collisions:0 txqueuelen:1000
          RX bytes:92353608 (88.0 MiB)  TX bytes:11164530 (10.6 MiB)

eth1      Link encap:Ethernet  HWaddr xx:xx:xx:xx:xx:xx
          inet addr:128.66.100.10  Bcast:128.66.100.255  Mask:255.255.255.0
          UP BROADCAST RUNNING MULTICAST  MTU:1500  Metric:1
          RX packets:816 errors:0 dropped:0 overruns:0 frame:0
          TX packets:486 errors:0 dropped:0 overruns:0 carrier:0
          collisions:0 txqueuelen:1000
          RX bytes:149517 (146.0 KiB)  TX bytes:34107 (33.3 KiB)

eth2      Link encap:Ethernet  HWaddr xx:xx:xx:xx:xx:xx
          inet addr:203.0.113.1  Bcast:203.0.113.255  Mask:255.255.255.0
          UP BROADCAST RUNNING MULTICAST  MTU:1500  Metric:1
          RX packets:738 errors:0 dropped:0 overruns:0 frame:0
          TX packets:880 errors:0 dropped:0 overruns:0 carrier:0
          collisions:0 txqueuelen:1000
          RX bytes:423603 (413.6 KiB)  TX bytes:94555 (92.3 KiB)

eth3      Link encap:Ethernet  HWaddr xx:xx:xx:xx:xx:xx
          inet addr:128.66.200.10  Bcast:128.66.200.255  Mask:255.255.255.0
          UP BROADCAST RUNNING MULTICAST  MTU:1500  Metric:1
          RX packets:611 errors:0 dropped:0 overruns:0 frame:0
          TX packets:182 errors:0 dropped:0 overruns:0 carrier:0
          collisions:0 txqueuelen:1000
          RX bytes:43921 (42.8 KiB)  TX bytes:13614 (13.2 KiB)

eth4      Link encap:Ethernet  HWaddr xx:xx:xx:xx:xx:xx
          inet addr:198.51.100.206  Bcast:198.51.100.255  Mask:255.255.255.0
          UP BROADCAST RUNNING MULTICAST  MTU:1500  Metric:1
          RX packets:183427 errors:0 dropped:0 overruns:0 frame:0
          TX packets:83 errors:0 dropped:0 overruns:0 carrier:0
          collisions:0 txqueuelen:1000
          RX bytes:85706791 (81.7 MiB)  TX bytes:3906 (3.8 KiB)

lo        Link encap:Local Loopback
          inet addr:127.0.0.1  Mask:255.0.0.0
          UP LOOPBACK RUNNING  MTU:65536  Metric:1
          RX packets:252 errors:0 dropped:0 overruns:0 frame:0
          TX packets:252 errors:0 dropped:0 overruns:0 carrier:0
          collisions:0 txqueuelen:1
          RX bytes:22869 (22.3 KiB)  TX bytes:22869 (22.3 KiB)
root@my-host:~#
root@my-host:~# route -n
Kernel IP routing table
Destination     Gateway         Genmask         Flags Metric Ref    Use Iface
192.0.2.0       0.0.0.0         255.255.255.0   U     0      0        0 eth0
128.66.100.0    0.0.0.0         255.255.255.0   U     0      0        0 eth1
203.0.113.0     0.0.0.0         255.255.255.0   U     0      0        0 eth2
128.66.200.0    0.0.0.0         255.255.255.0   U     0      0        0 eth3
198.51.100.0    0.0.0.0         255.255.255.0   U     0      0        0 eth4
root@my-host:~#
root@my-host:~# route -v add 128.66.200.10/32 gw 128.66.100.1
root@my-host:~# route -v add 128.66.100.10/32 gw 128.66.200.1
root@my-host:~#
root@my-host:~# route -n
Kernel IP routing table
Destination     Gateway         Genmask         Flags Metric Ref    Use Iface
192.0.2.0       0.0.0.0         255.255.255.0   U     0      0        0 eth0
203.0.113.0     0.0.0.0         255.255.255.0   U     0      0        0 eth2
198.51.100.0    0.0.0.0         255.255.255.0   U     0      0        0 eth4
128.66.100.0    0.0.0.0         255.255.255.0   U     0      0        0 eth1
128.66.100.10   128.66.200.1    255.255.255.255 UGH   0      0        0 eth3
128.66.200.0    0.0.0.0         255.255.255.0   U     0      0        0 eth3
128.66.200.10   128.66.100.1    255.255.255.255 UGH   0      0        0 eth1
root@my-host:~#
root@my-host:~# ping -c 3 128.66.100.10
PING 128.66.100.10 (128.66.100.10) 56(84) bytes of data.
64 bytes from 128.66.100.10: icmp_seq=1 ttl=64 time=0.008 ms
64 bytes from 128.66.100.10: icmp_seq=2 ttl=64 time=0.014 ms
64 bytes from 128.66.100.10: icmp_seq=3 ttl=64 time=0.017 ms

--- 128.66.100.10 ping statistics ---
3 packets transmitted, 3 received, 0% packet loss, time 1998ms
rtt min/avg/max/mdev = 0.008/0.013/0.017/0.003 ms
root@my-host:~#

星期四,2017 年 8 月 17 日 上午 8:12 PDT 更新

根据 dirkt 的要求,我正在详细说明我们的架构和我提出问题的原因。

本文讨论的虚拟主机(即具有网络接口 eth1、eth3 和三个与我的问题无关的其他网络接口的主机)用于测试我们设置的物理有线 TCP/IP 网络基础设施。具体来说,我们正在测试的是此 TCP/IP 网络基础设施的路由功能。

我们以前有两个虚拟主机,而不是我在原帖中描述的一个。这两个主机之间的 ping 操作就是我们的烟雾测试,以确保测试中的 TCP/IP 网络基础设施仍在运行。

由于无法详述的原因,拥有两台主机使得我们很难收集所需的日志。因此,我们切换到一台主机,为其提供两个 NIC,设置静态路由,以便发往 NIC 2 的任何内容都会从 NIC 1 传出,反之亦然。问题是,正如我所说,它们没有传出。

多年来,这种单主机/双网卡设置在 Windows 下一直有效。我不知道这是因为 Windows 有问题,我们无意中利用了错误,还是因为 Windows 没问题(即符合 RFC),我们只需要在 Linux VM 上正确配置即可获得相同的行为。

总结并提炼上面很长的一段 shell 文本:

两个接口:

eth1: 128.66.100.10/24; the router on this interface's network has IP address 128.66.100.1
eth3: 128.66.200.10/24; the router on this interface's network has IP address 128.66.200.1

相关路线:

Destination     Gateway         Genmask         Flags Metric Ref    Use Iface
128.66.100.0    0.0.0.0         255.255.255.0   U     0      0        0 eth1
128.66.100.10   128.66.200.1    255.255.255.255 UGH   0      0        0 eth3
128.66.200.0    0.0.0.0         255.255.255.0   U     0      0        0 eth3
128.66.200.10   128.66.100.1    255.255.255.255 UGH   0      0        0 eth1

我正在执行的命令:

ping -c 3 128.66.100.10

目的地 128.66.100.10 与上述两条路由匹配:

Destination     Gateway         Genmask         Flags Metric Ref    Use Iface
128.66.100.0    0.0.0.0         255.255.255.0   U     0      0        0 eth1
128.66.100.10   128.66.200.1    255.255.255.255 UGH   0      0        0 eth3

具有最长前缀匹配的路由是:

Destination     Gateway         Genmask         Flags Metric Ref    Use Iface
128.66.100.10   128.66.200.1    255.255.255.255 UGH   0      0        0 eth3

我想理解的是,为什么在存在这条路由的情况下,数据包不会从 eth3 传出,穿过我们的 TCP/IP 网络基础设施,然后返回并到达 eth1从外部

TCP/IP 堆栈显然没有查询转发表。就好像当它看到我正在 ping 本地连接的接口时,TCP/IP 堆栈只是说:“哦,这是本地接口。所以,我不会查询转发表。相反,我只会将回显回复发送回堆栈”。

我期望的行为是否符合 RFC?如果不符合,我必须放弃尝试。但如果符合 RFC,我想了解如何配置 Linux TCP/IP 堆栈以允许此行为。

2017 年 8 月 21 日星期一更新

我发现了 sysctlrp_过滤器接受本地内核参数。我将其设置如下:

root@my-host:~# cat /proc/sys/net/ipv4/conf/eth1/accept_local
1
root@my-host:~# cat /proc/sys/net/ipv4/conf/eth3/accept_local
1
root@my-host:~# cat /proc/sys/net/ipv4/conf/all/accept_local
1
root@my-host:~# cat /proc/sys/net/ipv4/conf/default/accept_local
1
root@my-host:~# cat /proc/sys/net/ipv4/conf/eth1/rp_filter
0
root@my-host:~# cat /proc/sys/net/ipv4/conf/eth3/rp_filter
0
root@my-host:~# cat /proc/sys/net/ipv4/conf/all/rp_filter
0
root@my-host:~# cat /proc/sys/net/ipv4/conf/default/rp_filter
0

设置此内核参数,重新启动,验证它们在重新启动后仍然存在,并再次测试显示行为没有差异。

请注意我的主机是在 Proxmox 4.4 下运行的 lxc Linux 容器。我还设置了rp_过滤器接受本地如上所示,在虚拟机管理程序接口上,对应于 eth1 和 eth3 接口我的主机

重新总结一下我的目标,我有一台 Linux 主机,上面有两个网卡,eth1 和 eth3。我尝试 ping eth1,让 ping 数据包通过测试中的 TCP/IP 网络基础设施路由,然后返回 eth3。

我尝试了上述所有方法,但都无法实现。我该怎么做?

2017 年 8 月 27 日更新

根据 dirkt 的说明,我没有提到 eth1 和 eth3 是否纯虚拟,或者它们是否对应于物理接口... eth1 和 eth3 都对应于相同虚拟机管理程序上的物理接口。目的是让从 eth1 传出的数据包实际上物理上离开虚拟机管理程序盒,进入真实的 TCP/IP 网络,然后路由回来。

2017 年 8 月 27 日更新 #2

根据 dirkt 的说法,我研究了网络命名空间,因为它看起来很有前景。然而,它并不是“直接起作用”。

我正在使用 LXC 容器,似乎容器中存在的某些隔离机制阻止我创建网络命名空间。如果我不在容器中运行,我认为添加网络命名空间不会有问题。

我找到了一些关于如何在 LXC 容器中实现这一点的参考资料,但它们相当晦涩难懂。还没有完成,今天不得不放弃……如果有人对此有任何建议,请告知……

答案1

(由于评论,我将保留其他答案)。

任务描述:给定一个 LXC 容器中的单个虚拟主机,该主机具有两个网络接口eth1eth3,它们位于不同的 LAN 段上并通过路由器外部连接,如何实现从 离开eth3并返回eth1(或反之亦然)的“回旋镖” ping?

这里的问题在于,Linux 内核会检测到目标地址被分配到eth1,并且会尝试直接将数据包传送到eth1,即使路由表规定数据包应通过 进行路由eth3

无法直接删除 IP 地址eth1,因为必须应答 ping。因此,唯一的解决方案是以某种方式使用两个不同的地址(或将eth1eth3分开)。

其中一种方法是使用iptables,例如这个答案由 harrymc 在评论中链接。

我已经在我的计算机上测试了另一种方法,其设置如下,使用一个网络命名空间来模拟外部网络,并使用两个网络命名空间来分隔目标 IP 地址:

Routing NS     Main NS      Two NS's

+----------+                   +----------+
|   veth0b |--- veth0a ....... | ipvl0    |
| 10.0.0.1 |    10.0.0.254     | 10.0.0.2 |
|          |                   +----------+
|          |                   +----------+
|   veth1b |--- veth1a ....... | ipvl1    |
| 10.0.1.1 |    10.0.1.254     | 10.0.1.2 |
+----------+                   +----------+

Routing NS启用转发。附加10.0.*.2地址分配给IPVLAN设备,可以将其视为分配给所连接主接口的额外 IP 地址。有关 IPVLAN 的更多详细信息,例如这里. 创造像

ip link add ipvl0 link veth0a type ipvlan mode l2
ip link set ipvl0 netns nsx

nsx新的网络命名空间在哪里,那么在该命名空间中,

ip netns exec nsx ip addr add 10.0.0.2/24 dev ipvl0
ip netns exec nsx ip link set ipvl0 up
ip netns exec nsx ip route add default via 10.0.0.1 dev ipvl0

Main NS除了默认规则外,还有以下路由规则

ip route add 10.0.0.2/32 via 10.0.1.1 dev veth1a
ip route add 10.0.1.2/32 via 10.0.0.1 dev veth0a

然后ping 10.0.0.2将进行“回旋镖”往返,如在和上所见。tcpdump因此,使用此设置,就 ping 等而言,所有日志记录都可以从进行,但更复杂的测试等可能需要其他命名空间至少提供接收器等。veth0aveth1aMain NSnc

LXC 容器使用网络命名空间(和其他命名空间)。我对 LXC 容器不太熟悉,但如果在容器内创建新的网络命名空间被阻止,请从容器外部进行操作。首先使用

ip netns list

然后ip netns exec NAME_OF_LXC_NS ...按上述操作。您也可以延迟将eth1eth3移入 LXC 容器,并首先创建两个 IPVLAN,然后然后将其移入容器。根据需要编写脚本。

编辑

还有第三种变体,无需网络命名空间即可工作。诀窍是使用策略路由,并赋予本地查找比正常情况更高(“更差”)的优先级,并以不同的方式处理来自绑定到特定接口的套接字的数据包。这可以防止向本地地址传递数据包,而这正是问题的主要根源。

使用与上述相同的模拟设置(减去 IPVLAN),

ip rule add pref 1000 lookup local
ip rule del pref 0
ip rule add pref 100 oif veth0a lookup 100
ip rule add pref 100 oif veth1a lookup 101
ip route add default dev veth0a via 10.0.0.1 table 100
ip route add default dev veth1a via 10.0.1.1 table 101

命令

ping 10.0.1.254 -I veth0a
ping 10.0.0.254 -I veth1a

正确发出 ping 请求。要获得 ping 回复,必须禁用针对源欺骗的测试:

echo "0" > /proc/sys/net/ipv4/conf/veth{0,1}a/rp_filter
echo "1" > /proc/sys/net/ipv4/conf/veth{0,1}a/accept_local

我也尝试了ncsocat,但无法让它们工作,因为没有选项可以nc强制听众在特定设备上回答,虽然 有这样的选项socat,但似乎没有效果。

因此,使用此设置进行除 ping 之外的网络测试会受到一定限制。

答案2

总而言之,您有以下配置:

Host 1           Main Host            Host 2
  ethX -------- eth1   eth3 --------- ethY
       128.66.200.10   128.66.100.10

在主主机中,/proc/sys/net/ipv4/ip_forward已启用,并且您想要测试主机 1 和主机 2 之间的连接是否正常。

快速提醒一下 Linux 如何处理每个接口的 IP 数据包:

Linux 数据包处理

因此,来自物理层的传入数据包会穿过PREROUTING传入接口,然后按目的地路由,然后穿过POSTROUTING传出接口,最后传出到物理层。相反,应用程序会将ping数据包发送到OUTPUT链,然后进行路由(图中未显示),然后穿过链POSTROUTING,最后传出。

在这里我使用入口“进入物理层”,出口意思是“离开物理层”。

您尝试做的是以某种方式告诉 Linux 内核不要以这种方式处理数据包,而是模拟一个数据包侵入使用eth3该应用程序ping,然后应该路由到eth1,在那里出口

但那只是不起作用:应用程序通过OUTPUT链发送数据包。如果您使用此选项强制ping绑定到,Linux 将简单地决定这是数据包的错误接口并丢弃数据包。它永远不会尝试将数据包视为eth3-I侵入进入eth3

所以正常方式处理这个问题的方法是只需从主机 1 发送 ping,然后验证它是否到达主机 2(以及另一个方向)。简单易行,无需费心。

由于“主主机”是虚拟的,eth1eth3可能不是真实的接口(您没说)。如果它们只是 veth 对的一端,则很容易抓住另一端,然后只需在ping该端生成(无论它在哪里)。

如果你坚持出于某些原因,在“主主机”上测试所有内容时,您还可以通过一些扭曲并桥eth3接到其他接口 veth 对,然后ping在另一端veth-pair。由于数据包是从 veth 桥接过来的,因此它将被视为侵入变成eth3,这样就可以了。但这确实不必要地复杂。

我不知道还有其他方法可以模拟侵入郵件。

你可以尝试一些iptable魔法,但如果你试图测试您的网络连接,这是一个坏主意:您永远不会知道您的iptables规则是否也适用于实际流量,因为这不是您测试的。

相关内容