![Linux 内核可以在具有相同前缀的两个接口之间转发 IPv6 数据包吗?](https://linux22.com/image/740814/Linux%20%E5%86%85%E6%A0%B8%E5%8F%AF%E4%BB%A5%E5%9C%A8%E5%85%B7%E6%9C%89%E7%9B%B8%E5%90%8C%E5%89%8D%E7%BC%80%E7%9A%84%E4%B8%A4%E4%B8%AA%E6%8E%A5%E5%8F%A3%E4%B9%8B%E9%97%B4%E8%BD%AC%E5%8F%91%20IPv6%20%E6%95%B0%E6%8D%AE%E5%8C%85%E5%90%97%EF%BC%9F.png)
假设拓扑
A <=> eth0 eth1 <=> B
eth0 和 eth1 位于同一设备上,启用了 IPv6 转发,但这两个接口未置于桥接中。
如果 A、B、eth0 和 eth1 具有相同的地址前缀,例如它们的 IPv6 地址是
- fe80::2
- fe80::3
- fe80::4
- fe80::5
那么,A和B可以互相通信吗?
如果地址是全球单播地址怎么办?
答案1
第 2 层解决方案:桥接
如果eth0
和eth1
没有像问题中最初假设的那样配置为网桥的一部分,那么中间系统将对从 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::a
和2001: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 之间路由呢?这对我来说没有太大意义。