从以太网帧到虚拟交换机 - 揭秘数据链路层的奥秘
系列:Linux 网络子系统源码剖析篇号:第 4 篇 内核版本:Linux 5.10 LTS重点模块:以太网处理、网桥、虚拟网络设备
以太网帧的接收与处理流程
协议类型识别与分发机制
Linux 网桥的完整实现原理
MAC 地址学习与转发数据库(FDB)
生成树协议(STP/RSTP)实现
虚拟网络设备(veth、bridge、macvlan)
容器网络的底层原理
已阅读第 1-3 篇文章
了解以太网协议基础
理解二层交换原理
熟悉网络命名空间概念
约 60-70 分钟
源码位置:net/ethernet/eth.c
/*** eth_type_trans - 确定以太网帧的协议类型* @skb: 接收到的数据包* @dev: 接收设备** 这是二层处理的入口函数!* 驱动在接收数据后会调用此函数** 返回:协议类型(网络字节序)*/
/*** __netif_receive_skb_core - 协议栈接收核心函数* @skb: 接收到的数据包* @pfmemalloc: 是否使用紧急内存** 这个函数负责将数据包分发到正确的协议处理函数*/
功能:• MAC 地址学习:记录每个端口后面的 MAC 地址• 帧转发:根据目的 MAC 转发到正确的端口• 泛洪:未知目的地址时广播到所有端口• 生成树:防止环路• VLAN 过滤:支持 VLAN 感知
/*** struct net_bridge - 网桥设备* 源码位置:net/bridge/br_private.h*//*** struct net_bridge_port - 网桥端口*/
/*** br_handle_frame - 网桥帧处理入口* @skb: 接收到的数据包** 这个函数在 __netif_receive_skb_core() 中被调用* 决定如何处理到达网桥端口的帧*/
/*** br_fdb_update - 更新 FDB(转发数据库)* @br: 网桥* @source: 源端口* @addr: MAC 地址* @vid: VLAN ID* @added_by_user: 是否用户添加** MAC 地址学习的核心函数*//*** br_fdb_find - 在 FDB 中查找 MAC 地址* @br: 网桥* @addr: MAC 地址* @vid: VLAN ID** 返回:FDB 表项,如果未找到返回 NULL*//*** FDB 老化机制** 定期清理长时间未使用的 FDB 表项*/
/*** br_handle_frame_finish - 完成帧处理* @skb: 数据包* @p: 接收端口** 决定如何转发帧*//*** br_forward - 转发帧到指定端口* @to: 目标端口* @skb: 数据包* @local_rcv: 是否本地接收* @local_orig: 是否本地发出*//*** br_flood - 泛洪(广播到所有端口)* @br: 网桥* @skb: 数据包* @pkt_type: 包类型* @local_rcv: 是否本地接收* @local_orig: 是否本地发出*/
/*** br_stp_rcv - 接收 STP BPDU* @p: 接收端口* @skb: BPDU 数据包*//*** br_stp_handle_bpdu - 处理配置 BPDU* @p: 接收端口* @bpdu: BPDU 内容*//*** br_stp_set_port_state - 设置端口状态* @p: 端口* @state: 新状态*/
特点:• 总是成对创建• 数据从一端进,另一端出• 常用于容器网络• 可以跨网络命名空间应用场景:• 容器与宿主机通信• 容器之间通信• 网络命名空间互联
源码位置:drivers/net/veth.c
/*** struct veth_priv - veth 私有数据*/struct veth_priv {struct net_device__rcu *peer; /* 对端设备 */atomic64_t dropped; /* 丢包统计 */struct bpf_prog__rcu *_xdp_prog; /* XDP 程序 */struct veth_rq *rq; /* 接收队列 */unsigned int requested_headroom;};/*** veth_xmit - veth 发送函数* @skb: 要发送的数据包* @dev: 发送设备** 这是 veth 的核心:将数据直接传给对端*//*** veth_newlink - 创建 veth pair* @src_net: 源网络命名空间* @dev: 第一个设备* @tb: netlink 属性* @data: 配置数据* @extack: 扩展错误信息*/
/*** 创建网桥设备*//*** 添加端口到网桥*/

工作流程:1. 创建容器时,创建 veth pair2. 一端放入容器的网络命名空间(eth0)3. 另一端连接到 docker0 网桥(veth0)4. 容器通过 veth pair 与宿主机通信5. docker0 网桥负责容器间通信6. NAT 规则实现容器访问外网
# 1. 创建网络命名空间ip netns add container1# 2. 创建 veth pairip link add veth0 type veth peer name veth1# 3. 将 veth1 移入容器命名空间ip link set veth1 netns container1# 4. 配置容器内的网卡ip netns exec container1 ip addr add 172.17.0.2/16 dev veth1ip netns exec container1 ip link set veth1 upip netns exec container1 ip link set lo up# 5. 将 veth0 连接到网桥ip link set veth0 master docker0ip link set veth0 up# 6. 配置默认路由ip netns exec container1 ip route add default via 172.17.0.1# 7. 配置 NAT(让容器访问外网)iptables -t nat -A POSTROUTING -s172.17.0.0/16 ! -o docker0 -j MASQUERADE
# 问题:容器无法互相通信# 1. 检查网桥状态brctl show# 或ip link show type bridge# 2. 检查端口状态brctl showstp docker0# 3. 检查 FDB 表bridge fdb show br docker0# 4. 检查 iptables 规则iptables -t filter -L FORWARD -v-n# 5. 抓包分析tcpdump -i docker0 -nn# 常见问题:# • 端口处于 BLOCKING 状态 → 检查 STP 配置# • FDB 表项错误 → 清空 FDB 重新学习# • iptables DROP → 检查 FORWARD 链规则
# 1. 禁用 STP(如果不需要)brctl stp docker0 off# 2. 调整 FDB 老化时间ip link set docker0 type bridge ageing_time 30000# 3. 启用硬件卸载ethtool -K docker0 tx-checksum-offload onethtool -K docker0 rx-checksum-offload on# 4. 调整转发延迟ip link set docker0 type bridge forward_delay 400# 5. 启用 VLAN 过滤(如果需要)ip link set docker0 type bridge vlan_filtering 1
# 1. 查看 veth pair 对端ip link show type vethethtool -S veth0 # 查看统计信息# 2. 查找 veth 的对端# 方法1:通过 ifindexip link | grep-A1 veth0# 方法2:通过 ethtoolethtool -S veth0 | grep peer_ifindex# 3. 检查 veth 是否在网桥中bridge link show# 4. 测试 veth 连通性# 在一端发送,另一端抓包ip netns exec ns1 ping-c1172.17.0.2 &tcpdump -i veth0 -nn icmp
/*** 网桥性能优化技巧*/// 1. 使用硬件卸载// 如果网卡支持,启用硬件网桥功能// 可以显著提高性能// 2. 调整 FDB 大小// 根据网络规模调整 FDB 哈希表大小#define BR_HASH_BITS 8 // 默认 256 个桶// 可以增加到 10 或 12(1024 或 4096 个桶)// 3. 禁用不必要的功能// • 如果不需要 STP,禁用它// • 如果不需要 VLAN 过滤,禁用它// • 减少功能检查,提高转发速度// 4. 使用多队列// 为网桥端口启用多队列// 利用多核 CPU 并行处理// 5. 调整老化时间// 根据网络特点调整 MAC 地址老化时间// 稳定网络:增加老化时间(减少学习开销)// 动态网络:减少老化时间(及时更新)
Q1: 网桥和路由器有什么区别?
A:
网桥工作在二层(数据链路层),根据 MAC 地址转发
路由器工作在三层(网络层),根据 IP 地址路由
网桥不改变数据包,路由器会修改 MAC 地址
网桥用于同一网段,路由器用于不同网段
Q2: 为什么需要生成树协议?
A: 防止网桥环路。如果网络中有环路,广播帧会无限循环,导致广播风暴。STP 通过阻塞冗余链路,确保网络拓扑无环。
Q3: veth pair 和物理网卡有什么区别?
A:
veth 是纯软件实现,没有硬件
veth 总是成对出现,数据从一端进另一端出
veth 常用于容器网络,连接不同的网络命名空间
veth 性能取决于 CPU,物理网卡有专用硬件
Q4: 如何选择 macvlan 还是 ipvlan?
A:
macvlan:需要不同 MAC 地址,交换机支持多 MAC
ipvlan:共享 MAC 地址,交换机限制 MAC 数量
ipvlan L3 模式:需要三层路由,性能更好
一般推荐 ipvlan,更简单且兼容性好
Q5: Docker 网桥模式的性能如何?
A:
网桥模式有一定性能损耗(~10-20%)
主要开销在 veth pair 和网桥转发
对于高性能需求,可以使用 host 模式或 macvlan
对于大多数应用,网桥模式性能足够
Documentation/networking/bridge.txt - 网桥文档
Documentation/networking/veth.txt - veth 文档
Documentation/networking/device_drivers/ - 设备驱动文档
《Linux 网桥实现原理》
《容器网络深度解析》
《IEEE 802.1D 标准》- 生成树协议
《IEEE 802.1Q 标准》- VLAN
# 网桥管理brctl # 传统网桥工具ip link # 现代网络工具bridge # 网桥专用工具# 调试工具tcpdump # 抓包分析wireshark # 图形化抓包ethtool # 网卡配置
第 5 篇:IP 层实现(上)- 接收与路由
我们将深入分析:
IP 报文接收流程(ip_rcv)
IP 头校验与选项处理
路由子系统(FIB trie)
路由查找算法
转发与本地接收决策
分片重组机制
敬请期待!
作者:肇中
💡 提示:本文基于 Linux 5.10 LTS 内核,不同版本可能有差异。
📧 反馈:如有问题或建议,欢迎交流讨论。
⭐ 如果觉得有帮助,欢迎分享给更多人!