当前位置:首页>Linux>Linux 网络子系统源码剖析(四):二层处理与网桥

Linux 网络子系统源码剖析(四):二层处理与网桥

  • 2026-09-08 22:18:11
Linux 网络子系统源码剖析(四):二层处理与网桥

从以太网帧到虚拟交换机 - 揭秘数据链路层的奥秘

系列:Linux 网络子系统源码剖析篇号:第 4 篇 内核版本:Linux 5.10 LTS重点模块:以太网处理、网桥、虚拟网络设备


📋 本篇导读

你将学到

  • 以太网帧的接收与处理流程

  • 协议类型识别与分发机制

  • Linux 网桥的完整实现原理

  • MAC 地址学习与转发数据库(FDB)

  • 生成树协议(STP/RSTP)实现

  • 虚拟网络设备(veth、bridge、macvlan)

  • 容器网络的底层原理

前置知识

  • 已阅读第 1-3 篇文章

  • 了解以太网协议基础

  • 理解二层交换原理

  • 熟悉网络命名空间概念

阅读时间

约 60-70 分钟


🎯 二层处理在网络子系统中的位置

网络协议栈分层

二层处理的核心职责


📦 以太网帧处理

以太网帧结构

eth_type_trans() - 协议类型识别

源码位置:net/ethernet/eth.c

/**
* eth_type_trans - 确定以太网帧的协议类型
* @skb: 接收到的数据包
* @dev: 接收设备
* 
* 这是二层处理的入口函数!
* 驱动在接收数据后会调用此函数
* 
* 返回:协议类型(网络字节序)
*/

协议分发机制

/**
* __netif_receive_skb_core - 协议栈接收核心函数
* @skb: 接收到的数据包
* @pfmemalloc: 是否使用紧急内存
* 
* 这个函数负责将数据包分发到正确的协议处理函数
*/

协议注册机制


🌉 Linux 网桥实现

网桥架构

功能:
• 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() 中被调用
* 决定如何处理到达网桥端口的帧
*/

MAC 地址学习与 FDB

/**
* 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: 是否本地发出
*/

🌳 生成树协议(STP/RSTP)

STP 基本概念

STP 实现

/**
* br_stp_rcv - 接收 STP BPDU
* @p: 接收端口
* @skb: BPDU 数据包
*/
/**
* br_stp_handle_bpdu - 处理配置 BPDU
* @p: 接收端口
* @bpdu: BPDU 内容
*/
/**
* br_stp_set_port_state - 设置端口状态
* @p: 端口
* @state: 新状态
*/

🔌 虚拟网络设备

veth pair - 虚拟以太网对

特点:
• 总是成对创建
• 数据从一端进,另一端出
• 常用于容器网络
• 可以跨网络命名空间
应用场景:
• 容器与宿主机通信
• 容器之间通信
• 网络命名空间互联

veth 实现

源码位置: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: 扩展错误信息
*/

bridge 虚拟交换机

/**
* 创建网桥设备
*/
/**
* 添加端口到网桥
*/

macvlan/ipvlan

🐳 容器网络原理

Docker 网桥模式

工作流程:
1. 创建容器时,创建 veth pair
2. 一端放入容器的网络命名空间(eth0)
3. 另一端连接到 docker0 网桥(veth0)
4. 容器通过 veth pair 与宿主机通信
5. docker0 网桥负责容器间通信
6. NAT 规则实现容器访问外网

创建容器网络的步骤

# 1. 创建网络命名空间
ip netns add container1
# 2. 创建 veth pair
ip 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 veth1
ip netns exec container1 ip link set veth1 up
ip netns exec container1 ip link set lo up
# 5. 将 veth0 连接到网桥
ip link set veth0 master docker0
ip 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:排查网桥转发问题

# 问题:容器无法互相通信
# 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 链规则

案例2:优化网桥性能

# 1. 禁用 STP(如果不需要)
brctl stp docker0 off
# 2. 调整 FDB 老化时间
ip link set docker0 type bridge ageing_time 30000
# 3. 启用硬件卸载
ethtool -K docker0 tx-checksum-offload on
ethtool -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

案例3:调试 veth pair

# 1. 查看 veth pair 对端
ip link show type veth
ethtool -S veth0  # 查看统计信息
# 2. 查找 veth 的对端
# 方法1:通过 ifindex
ip link | grep-A1 veth0
# 方法2:通过 ethtool
ethtool -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 地址老化时间
// 稳定网络:增加老化时间(减少学习开销)
// 动态网络:减少老化时间(及时更新)

📝 总结

核心要点回顾

数据流总结


❓ 常见问题(FAQ)

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/ - 设备驱动文档

源码位置

推荐阅读

  1. 《Linux 网桥实现原理》

  2. 《容器网络深度解析》

  3. 《IEEE 802.1D 标准》- 生成树协议

  4. 《IEEE 802.1Q 标准》- VLAN

实用工具

# 网桥管理
brctl          # 传统网桥工具
ip link        # 现代网络工具
bridge         # 网桥专用工具
# 调试工具
tcpdump        # 抓包分析
wireshark      # 图形化抓包
ethtool        # 网卡配置

🎯 下一篇预告

第 5 篇:IP 层实现(上)- 接收与路由

我们将深入分析:

  • IP 报文接收流程(ip_rcv)

  • IP 头校验与选项处理

  • 路由子系统(FIB trie)

  • 路由查找算法

  • 转发与本地接收决策

  • 分片重组机制

敬请期待!


作者:肇中 


💡 提示:本文基于 Linux 5.10 LTS 内核,不同版本可能有差异。

📧 反馈:如有问题或建议,欢迎交流讨论。

⭐ 如果觉得有帮助,欢迎分享给更多人!

最新文章

随机文章