Linux 内核可以在具有相同前缀的两个接口之间转发 IPv6 数据包吗?

Linux 内核可以在具有相同前缀的两个接口之间转发 IPv6 数据包吗?

假设拓扑

A <=> eth0 eth1 <=> B

eth0 和 eth1 位于同一设备上,启用了 IPv6 转发,但这两个接口未置于桥接中。

如果 A、B、eth0 和 eth1 具有相同的地址前缀,例如它们的 IPv6 地址是

  • fe80::2
  • fe80::3
  • fe80::4
  • fe80::5

那么,A和B可以互相通信吗?

如果地址是全球单播地址怎么办?

答案1

第 2 层解决方案:桥接

如果eth0eth1没有像问题中最初假设的那样配置为网桥的一部分,那么中间系统将对从 A 到 B 的流量不透明。但是,如果将这些接口配置为网桥的两个部分,那么流量就可以从 A 传输到 B 和从 B 传输到 A,而无需对 A 或 B 的配置进行任何更改。

更棘手的第三层替代方案:转发

使用转发使 A 和 B 之间的流量通过中间设备将更加困难,因为在问题中指定的条件下,转发不是完成这项工作的合适工具。

首先,由于 fe80:: 是链路本地前缀,因此任何具有该前缀的流量都不会从一个链路(例如 A <=> eth0)转发到另一个链路(eth1 <=> B);这就是“链路本地”被定义为。但是,我们假设您使用其他前缀,但其他设置保持不变。

中间设备上的内核必须先接收流量包才能转发它。如果你不使用桥接,那么第 2 层将阻止你:除非你明确告诉设备 A 它必须使用 eth0 才能到达设备 B(= 在设备 A 上为设备 B 设置主机路由,使用 eth0 作为网关),否则设备 A 将看到设备 B 的 IPv6 地址具有与设备 A 本身相同的前缀。

通常情况下,这意味着设备 B 应该在同一网段上直接可达,因此设备 A 只需在 A <=> eth0 链路上发送 ICMPv6 邻居发现(NDP)数据包(也称为邻居请求),询问设备 B 的 MAC 地址。

邻居请求数据包的目标地址将是“请求节点”多播地址,该地址将位于 FF02:0:0:0:0:1:FF00::/104 前缀内。初始 FF02 表示它是本地链路多播,因此惯于被中间设备转发。因此设备 B 永远不会收到它,也无法应答它。

经过几次重试后,设备 A 将得出结论:其邻居请求没有得到响应,这意味着设备 B 无法访问。中间设备永远没有机会转发任何内容,因为它永远不会收到任何允许转发的内容。

即使设备 A 已经知道设备 B 的 MAC 地址,eth0 上的 MAC 过滤器(通常在网络接口的硬件中)也不会将数据包传递给中间设备的内核。基本上:“这不是我的地址,也不是我需要关心的多播/广播地址;我不需要进一步查看它。”

但是,如果您在设备 A 的路由表中添加一个优先级高于本地网段默认条目(即更紧密的前缀)的条目,即“要到达设备 B,必须使用 eth0 作为网关”,则设备 A 将首先使用 NDP 获取 eth0 的 MAC 地址,并使用设备 B 的第 3 层 IPv6 地址但使用 eth0 的第 2 层 MAC 地址将数据包发送到设备 B。

现在流量到达中间设备的 eth0,通过第 2 层 MAC 过滤器,然后内核开始转发它。“这是我的 MAC 地址,但不是我的 IPv6 地址,所以它应该被转发。”但内核如何决定使用哪个接口来转发数据包?好吧,它使用它的路由表

在默认路由规则(也称为弱主机模型)下,内核将只选择路由表中前缀与目标地址匹配的第一行,并使用它来转发流量。但是您将有两个具有完全相同前缀的接口,并且可能还具有相同的度量值,因此它有 50% 的机会使用 eth1,这通常取决于最后配置的接口。

假设您很幸运,路由表中 eth1 是您的前缀的最顶层路由。数据包将进入 eth1 的传出队列,内核将首先发出 NDP 查询,查询 eth1 上设备 B 的 MAC 地址。设备 B 将应答该查询,然后数据包将通过 eth1 发出,其中包含设备 B 的目标 IPv6 地址以及设备 B 的第 2 层 MAC 地址。最后,设备 B 可以接收该数据包。

但等等,这只是上半场问题!答案还必须能够从 B 通过中间设备到达 A。因此它将面临同样的挑战:首先设备 B 必须知道“要到达设备 A,数据包必须发送到 eth1,而不是直接发送到设备 A 的 MAC 地址。”

还有一个挑战。还记得 eth1 是中间设备上网络前缀的出站流量的首选吗?现在答案是相反的,这将通过 eth1 将答案发回,这对它来说是错误的。

要解决这个问题,你需要在中间设备上使用主机路由,或者更普遍地说,为通过 eth1 进入的任何流量准备一个单独的路由表。这将需要高级路由规则(实际上,您将实施一种替代路由策略,称为强主机模型。)

那么你也可以通过转发来实现,但是需要很多额外的配置才能使其工作,通常不值得付出努力。

这实际上是为什么良好的网络设计和规划很重要:如果您构建您的网络前缀,使得它们与您的网络的实际拓扑相匹配,那么让数据包到达目的地就会容易得多。

答案2

fe80::/10是链路本地地址。始终处于链路上,从不路由;仅在本地段范围内。它们是“下一跳”地址,是 IP 协议中发现并与邻居(包括路由器)通信的一种方式。

您指定未桥接,因此 A 和 B 不是通过第 2 层的邻居。


跨越第 2 层边界是第 3 层路由器的工作。您的示例没有这样的路由,因此根据 rfc4861,以下是通常的路由方式。

节点 A 和 B 配置额外的 IP 地址,超出全局单播或唯一本地地址空间。假设2001:db8:0:7224::a2001:db8:0:7224::b

  • 如果 A 确定 B 处于链接状态,则下一跳直接到2001:db8:0:7224::b
  • 如果不在链路上,则选择路由器。这可以通过链路本地实现,可能fe80::3在 eth0 端。目标标头仍然是2001:db8:0:7224::b,因为作为路由器,它会将数据包转发到其最终目的地。

无论哪种情况,邻居发现都会确定包装 IP 数据包的链路层地址。

答案3

这本身并不是一个答案,这只是我如何很好地桥接有线和无线网络。对于 IPv4,我有一个手动 IP 地址,对于 IPv6,我启用了自动配置。自动配置可以很好地通过 br0。如果您在 pi 上运行 radvd(我这样做了),您需要提及 br0 作为接口,否则您不必担心。

L2 桥接不能只桥接 IPv4 而不桥接 IPv6,因为桥接实际上是在以太网数据包上工作的,而 IPv4 和 IPv6 是在之上以太网。要么全有,要么全无——要么同时桥接,要么两个都不桥接。

auto eth0
iface eth0 inet manual

auto wlan0
iface wlan0 inet manual

auto br0
iface br0 inet static
»       bridge_ports eth0 wlan0
»       address 192.168.1.72
»       netmask 255.255.255.0
»       gateway 192.168.1.1
iface br0 inet6 auto
»       bridge_ports eth0 wlan0 # Configured via radvd on this same host

您说 eth0 是 WAN,wlan 是 LAN。这让事情变得复杂,因为您根本无法正确地桥接它们,否则会出现严重问题。在这种情况下,您必须转发。但那么您为什么要在 IPv6 上在 LAN 和 WAN 之间路由呢?这对我来说没有太大意义。

相关内容