无法使用桥接网络在两个网络命名空间之间建立通信

无法使用桥接网络在两个网络命名空间之间建立通信

我正在跟进这个视频关于Linux中的网络命名空间,并尝试自己实践,但由于某种原因它不起作用。我所做的是:

创建 2 个名为“blue”和“red”的网络命名空间后:

sudo ip link add bridge type bridge
sudo ip link set dev bridge up
sudo ip link add veth-red type veth peer name veth-red-br
sudo ip link add veth-blue type veth peer name veth-blue-br
sudo ip link set veth-red netns red
sudo ip link set veth-red-br master bridge
sudo ip link set veth-blue netns blue
sudo ip link set veth-blue-br master bridge
sudo ip netns exec red ip addr add 192.168.15.1/24 dev veth-red
sudo ip netns exec blue ip addr add 192.168.15.2/24 dev veth-blue
sudo ip netns exec red ip link set veth-red up
sudo ip link set veth-red-br up
sudo ip netns exec blue ip link set veth-blue up
sudo ip link set veth-blue-br up

现在,如果我尝试从红色 ping 到蓝色,反之亦然,则不起作用。奇怪的是,ARP 确实有效,因此接口之间存在连接。我知道这一点是因为当我arp在命名空间中运行时我可以看到正确的值:

$ sudo ip netns exec red arp
Address                  HWtype  HWaddress           Flags Mask            Iface
192.168.15.2             ether   ca:4f:b6:65:a0:f8   C                     veth-red

$ sudo ip netns exec blue ifconfig
veth-blue: flags=4163<UP,BROADCAST,RUNNING,MULTICAST>  mtu 1500
        inet 192.168.15.2  netmask 255.255.255.0  broadcast 0.0.0.0
        inet6 fe80::c84f:b6ff:fe65:a0f8  prefixlen 64  scopeid 0x20<link>
        ether ca:4f:b6:65:a0:f8  txqueuelen 1000  (Ethernet)
        RX packets 85  bytes 8946 (8.9 KB)
        RX errors 0  dropped 0  overruns 0  frame 0
        TX packets 27  bytes 1986 (1.9 KB)
        TX errors 0  dropped 0 overruns 0  carrier 0  collisions 0

所以你可以看到红色命名空间在其 ARP 表中具有蓝色命名空间 veth 的 MAC 地址,那么为什么 ping 不起作用呢?

答案1

那是因为 IPv4 被过滤了iptables或者nftables。你可能会想:不可能,以太网桥工作在第 2 层,所以与iptables在第 3 层使用 IPv4。

但有一个办法:br_netfilter(显示通过回溯机因为他们让证书过期):

bridge-netfilter 代码启用以下功能:

{Ip,Ip6,Arp}表可以过滤桥接的 IPv4/IPv6/ARP 数据包,即使封装在 802.1Q VLAN 或 PPPoE 标头中也是如此。这启用了状态透明防火墙的功能。

因此,这 3 个工具的所有过滤、日志记录和 NAT 功能都可以在桥接帧上使用。

也可以看看基于 Linux 的桥上的 ebtables/iptables 交互了解详情。

所以如果有人这样做:

iptables -I FORWARD -j DROP
modprobe br_netfilter

(或者将 FORWARD 策略设置为 DROP,而没有规则接受数据包)则默认情况下,网桥将丢弃在第 2 层接收到的所有 IPv4 流量iptables:对于 IPv4 帧,br_netfilter将临时将第 2 层以太网帧“升级”为 IPv4 数据包,并将其馈送到iptables,仍然在桥路上。丢弃的数据包iptables不会出桥。那些未丢弃的(在上面的示例中没有)将作为以太网帧反馈到网桥中以继续网桥处理。

可能的罪魁祸首:Docker。 Docker 确实启用了br_netfilter(可能是为了容器间隔离)。没有明确记录,但这是Docker 部分的来源:

  if config.EnableIPTables || config.EnableIP6Tables {
      if _, err := os.Stat("/proc/sys/net/bridge"); err != nil {
          if out, err := exec.Command("modprobe", "-va", "bridge", "br_netfilter").CombinedOutput(); err != nil {
              logrus.Warnf("Running modprobe bridge br_netfilter failed with message: %s, error: %v", out, err)
          }
      }
  }

当它加载bridge内核模块时,它也会加载br_netfilter.

Docker确实设置了iptables' 将策略转发为 DROP:

路由器上的 Docker

Docker 还将链的策略设置FORWARDDROP.
[...]

即使这不是因为 Docker,您也肯定已经br_netfilter加载了(很可能是通过physdev然后)和防火墙规则丢弃数据包iptables' (或者nftables') 转发规则。否则你真的有桥牌规则,要么使用ebtables或者nftables家庭阻止转发帧。

在内核 5.3 之前,这可能是在桥接级别执行状态防火墙的唯一方法。

这个示意图蓝色字段(以太网)中的绿色(IPv4)框代表iptables(或者nftablesbr_netfilter)在启用它时在以太网桥接路径中运行:

Netfilter 和通用网络中的数据包流

有多种方法可以使用桥粒度而不是系统范围的粒度来执行此操作(最好在启动早期执行此操作,以便它也在新的命名空间中继承):

  • 加载br_netfilter但禁用其效果:

    modprobe br_netfilter
    sysctl -w net.bridge.bridge-nf-call-arptables=0
    sysctl -w net.bridge.bridge-nf-call-ip6tables=0
    sysctl -w net.bridge.bridge-nf-call-iptables=0
    

    在此刻红色的蓝色的能够通信,但 Docker(可能是损坏的最初原因)会随机损坏自身。

  • 在选择的网桥上重新启用它:

    ip link set bridge type bridge nf_call_iptables 1
    

    之间的通信将再次停止红色的蓝色的

从内核 5.3 开始,系统范围内设置获取每个网络命名空间,如果需要在非初始网络命名空间中禁用(或重新启用)此功能,从而影响其中的所有网桥:

ip netns FOO sysctl -w net.bridge.bridge-nf-call-iptables=X

Docker 不支持这些模式:它需要在它创建的所有桥上启用 sysctl(如果有创建桥的设置,则可能在它创建的网络命名空间上启用 sysctl),但目前不知道这一点。

当然,删除内核模块也会阻止交互并允许流量返回:

rmmod br_netfilter

仍然从内核 5.3 开始,nftables直接在网桥系列中获得状态防火墙支持,因此br_netfilter不再需要使用此功能。br_netfilter今天对于使用 sysctl 与 PPPoE 相关的防火墙net.bridge.bridge-nf-filter-pppoe-tagged(默认情况下未启用)可能仍然有用,据我所知,目前还没有简单的替代品。

另请参阅我在此处和 SF 上这些 Q/A 中的相关答案:

相关内容