一个网桥有三个端口,A、B、C 各占一个。A 发给 B 的帧第一次经过网桥时,网桥尚不知道 B 在哪个端口。它既不能向所有网络查询 B,也不能直接丢弃所有未知目的地,否则刚接入的设备就无法开始通信。未知单播泛洪和源地址学习共同解决这个起步问题。

本篇从这个三端口问题出发,再增加一个属于其他 VLAN 的 D。实验使用 Linux 虚拟机中的自建命名空间、veth 和网桥,直接发送带唯一标记的二层测试帧,检查学习前后的出口变化。它延续第 03 篇的观察边界:端口收到测试帧是链路层证据,不等于业务请求成功,更不能代表物理交换机性能。

帧里的地址决定什么

本实验构造未带 VLAN 标签的 Ethernet II 格式:六字节目的 MAC、六字节源 MAC、两字节 EtherType,后接测试载荷。Linux 的 struct ethhdr 就按这一顺序定义;附件按照这个已知格式构造和解析帧,不把十四字节偏移套到任意抓包链路类型。

1
2
3
目的 MAC          源 MAC            EtherType   测试载荷
02:00:00:00:04:02 02:00:00:00:04:01 88 b5 本次运行标记与填充
|------ 6 ------| |------ 6 ------| |-- 2 --| |------ 46 ------|

0x88b5 在 IANA 的 IEEE 802 编号表中是本地实验 EtherType,本篇不占用 IPv4 或 ARP 类型。六十字节是脚本提交给虚拟接口的长度,包含十四字节头和四十六字节测试载荷,不包含前导码与 FCS。实验未测量真实线缆上的帧长、间隔或校验序列。

目的 MAC 回答的是这个二层转发域内的接收对象。它不是 IP 地址,也不是进程端口。网桥可以在没有为这些接口配置 IPv4 地址的情况下转发本篇帧;后续篇章才讨论如何由 IP 目标确定下一跳,以及怎样得到下一跳的链路层地址。

源 MAC 则提供反向可达性的线索:如果源 A 的帧从端口 pa 进入,后续发往 A 的帧可以尝试从 pa 发出。这个学习规则不需要网桥理解测试载荷,更不需要先执行应用层握手。

从一次未知单播建立转发表

网桥维护转发数据库 FDB。Linux 网桥学习收到帧的源 MAC,并根据目的 MAC 查找出口。本实验开启 VLAN 过滤,因此观察 FDB 时还要带上 VLAN 语境,不能把不同 VLAN 中相同地址的记录混成一个全局位置。

设初始 FDB 中没有实验地址 A 和 B。A 从 pa 发给 B,网桥先获得“VLAN 10 的 A 位于 pa”这一信息。由于没有 B 的目的记录,它把未知单播发往其他合格出口。在本篇无环、转发状态正常且泛洪未关闭的配置中,pb 和 pc 都符合条件。

“合格出口”包含实际约束:端口属于对应 VLAN,允许转发,相关泛洪开关开启。Linux 还允许改变 hairpin 等端口行为。本篇使用普通非 hairpin 配置,不把“所有端口都会收到”当作与配置无关的定律。

B 随后从 pb 发给 A。网桥学习 B 位于 pb,同时已有 A 的记录,所以这次可以只转发到 pa。再让 A 发给 B,目的查找已经命中 pb,C 的端口就不再收到这份测试帧。

这里有一个容易漏掉的因果顺序:第一帧写着“目的 B”,并不使网桥学会 B 在哪里。B 所在端口的学习来自 B 作为源发送的帧。若 B 一直没有发送,A 连发两次不保证第二次已经变成定向转发。

FDB 也不是永久正确的静态地图。设备移动、记录老化、静态配置或安全策略都可能改变结果。本实验在几秒内完成,不测老化计时或移动收敛;排障时必须读取当时的表项,不能根据昨天成功通信的事实推断今天仍有相同出口。

广播与未知单播的区别

广播的目的 MAC 是 ff:ff:ff:ff:ff:ff,地址本身就指定广播语义。未知单播仍有一个单播目的地址,只是网桥暂时没有对应 FDB 项。两者可能在本次拓扑中产生相同出口集合,但产生集合的原因不同。

因此,对照实验要看两种输入。B 学习完成后,A 发往 B 的单播只出 pb;A 再发广播,pb 和 pc 仍然都能收到。不能因为前一轮已经学习 B,就预期广播也只到 B。

抓取未知单播还有一个实践陷阱:用 ping 作为“第一包”时,ARP 等前置通信可能已让网桥学习相关源地址。最终看见单播定向转发,不能据此否认未知单播泛洪。本实验直接构造二层帧,并在第一阶段前断言 FDB 不包含合成的 B 地址,避免把地址解析和桥学习混成一个不可控步骤。

VLAN 怎样约束出口

拓扑把 A、B、C 划入 VLAN 10,D 划入 VLAN 20。四条 veth 都连接同一个网桥,但广播域不是单由物理连线或接口数量决定的。

1
2
3
4
5
6
7
A -- pa ┐
B -- pb ├-- br0,启用 vlan_filtering
C -- pc ┤
D -- pd ┘

pa / pb / pc:VLAN 10,pvid,untagged
pd :VLAN 20,pvid,untagged

Linux bridge vlan 中,PVID 用于把未带标签的入站帧归入指定 VLAN;untagged 控制对应 VLAN 的帧从该端口出去时不带标签。这两个设置分别影响入站分类和出口表现,不能把它们当成一个同义开关。

附件创建网桥时显式使用 vlan_filtering 1vlan_default_pvid 0,然后逐端口配置成员关系。这避免依赖默认 VLAN 成员;Linux 的 VLAN 过滤默认并未开启。仅仅创建一个名称里带 VLAN 的接口,无法证明转发隔离已生效。

A 发出的未标记广播被归入 VLAN 10,所以 B、C 收到,D 没有收到。D 发给 A 的未标记帧被归入 VLAN 20,即使 FDB 在 VLAN 10 中已有 A,也不能直接拿那个表项把数据送到 VLAN 10。

本次所有出口都配置 untagged,观察到的测试帧没有 802.1Q 标签并不意味着没有 VLAN 隔离。相反,是否隔离要结合成员配置和跨域对照判断。此配置没有三层网关;若另行加入经过策略控制的三层转发,跨 VLAN 通信的结论必须重新验证。

VLAN 限制的是转发范围,不给帧提供密码学身份。实验能用原始套接字提交合成源地址,正说明不能仅靠 FDB 学习结果认定发送者已经通过认证。身份和访问控制留到第 37 篇。

真实隔离实验

附件 lab.py 只依赖 Python 标准库与已有 ipbridge 命令。运行位置必须是自有 Linux 实验虚拟机,需要创建命名空间、配置接口和原始套接字的权限。macOS 宿主不能直接执行此 Linux 实验;脚本会先拒绝非 Linux 或非 root 环境。

1
sudo python3 lab.py

脚本为每次运行生成 net04- 随机前缀,创建一个网桥命名空间和四个端点命名空间。所有 veth 都在这些新对象之间连接,没有把宿主接口加入网桥,没有创建默认路由,也没有向外部地址发包。

每阶段先启动各端口观察进程,收到 READY 后统一放行,再发送带该阶段唯一标记的帧。观察使用 Linux AF_PACKET 原始套接字,仅保存实验 EtherType 和本次标记匹配的字节。端点接口处于混杂模式,以便观察被网桥泛洪到该端口、目的地址却不是该端点的单播。

本次实际运行环境是 Linux 6.18.10-200.fc43.aarch64、Python 3.14.3、iproute2 6.14.0,运行在已有 Podman 本地虚拟机中。结果保存在 原始逐阶段记录,包含 VLAN 配置、阶段前后 FDB 和匹配帧的十六进制内容。

阶段 B 端口 C 端口 D 端口 观察含义
A→B,B 尚未学习 1 1 0 VLAN 10 内未知单播泛洪
A→B,B 已学习 1 0 0 FDB 命中后定向转发
A→广播 1 1 0 广播范围受 VLAN 限制

另外两个阶段也通过:B→A 只在 A 记录到一帧,C、D 均为零;D→A 时 A、B、C 都未记录到匹配帧。数字是本次每阶段一秒观察窗口内的匹配帧数,不是吞吐量或丢包率。有限时间内未观察到帧,必须和配置、正向对照及发送断言一起解释。

脚本结束时先回收自己启动的观察进程,再删除本次创建的命名空间,并检查这些名称已不存在。异常路径也进入同一清理段。不得用删除全部命名空间的命令替代它;同一虚拟机可能存在其他任务的资源。

二层环路为什么不能靠学习自行消失

把三个网桥连成三角形,且所有边都允许同一 VLAN 的广播转发。从第一个网桥注入一帧广播,两条方向各产生一份副本。下一跳仍会向另一个出口转发,经过剩余边后,副本能够回到原来的网桥。

即使源地址已经学到,广播仍不会变成定向单播。重复副本还会让同一源地址从不同端口出现,影响源地址学习。普通桥转发也没有像后续 IPv4 TTL 那样的逐跳递减字段,不能用三层路由的跳数限制来保证这个二层环自然停止。

这个推导依赖持续允许转发、没有额外环路抑制或速率限制等条件;实际设备可能丢弃或限制流量。因此不能从拓扑图直接填写一个广播风暴速率。本篇没有真的连出环路,也没有测量风暴。

生成树通过端口状态限制活动转发拓扑,保留连通性而排除某些形成环的路径。Linux 文档区分学习状态与转发状态:端口能学习并不等于已经转发数据。启用某个 STP 开关,也不能直接宣称 RSTP 或 MSTP 已运行、收敛时间达到了某个数值。

本次网桥显式关闭 STP,因为拓扑本身无环,目的是观察基本学习与 VLAN 行为。STP 的选举、故障切换和收敛均未实测。安全的后续验证需要另一套完全隔离的拓扑和有界发送,不能把这套学习实验接到真实交换网络上制造环路。

练习

学习顺序。 删除关于 B 的假设表项,保持 A→B 的发送方向不变。连续发送两帧,能否断定第二帧不会到 C?

核对:不能。两帧都只能提供关于源 A 的学习信息。除非期间 B 自己发帧、管理面安装表项或有其他已知状态变化,否则目的 B 仍可能未知。验证时应记录每阶段前的 FDB,而不只记录最后一张表。

VLAN 对照。 把 D 的端口从 VLAN 20 改为 VLAN 10 的成员,且其他条件与脚本一致。A 的广播应如何变化?如果 D 仍然没有观察到,下一项应该检查什么?

核对:D 应成为合格广播出口。仍未看到时,应核对端口实际成员、转发状态、广播泛洪开关、观察进程是否就绪以及匹配标记。一次零计数不能直接证明 VLAN 功能错误。

验证边界与一手资料

本篇完成的是 Linux 虚拟二层拓扑中的五组真实转发观察,并非纯模型。它没有验证物理以太网 FCS、网卡卸载、交换 ASIC、STP 收敛或生产性能,也不包含应用层端到端业务验收。

资料于 2026-09-19 当次核查。IEEE 802.1Q-2022 官方项目页确认桥接与 VLAN 标准范围,但本次没有取得完整规范正文,未引用未经核实的条号。具体实验行为以本次 Linux 版本、配置及记录为限。