大家好,我是Q,QQ邮箱:1042484520@qq.com。
今天我们在上一讲的基础上再扩展下 Brideg 增加和删除 VLAN 的代码流程。
话不多说,我们先直接展示网络架构图,具体如下所示:

在
Linux Bridge 中,VLAN 标签的增加和删除是通过内核的网络栈、VLAN 支持模块以及桥接(Bridge)驱动来实现的。具体来说,
VLAN 标签的处理 是由
bridge 模块 和
VLAN 子系统 协同工作完成的。
要深入了解
VLAN 标签的增加和删除 的过程,我们可以分为以下几个部分:
1. VLAN 子系统概述
在 Linux 中,
VLAN 子系统 通过 IEEE 802.1Q 标准来处理 VLAN 标签的插入和去除。VLAN 标签本质上是在
数据链路层 对以太网帧进行标记,使其可以被识别为属于特定 VLAN。
VLAN 标签的添加和删除主要通过以下几个组件完成:
- vlan 驱动程序:负责将 VLAN 标签添加到以太网帧中。
- bridge 驱动程序:负责在桥接接口之间转发数据,管理不同接口上的 VLAN 标签。
2. 增加和删除 VLAN 标签的工作原理
当桥接接口(br0)转发数据时,会根据接口是否支持 VLAN 来决定是否增加或删除 VLAN 标签。
(1)从 eth0.22 转发到 WLAN1(带 VLAN 标签的数据转发)
- 数据从 eth0.22 发出:
- 数据帧从 eth0.22 接口发出时,包含 VLAN 标签。eth0.22 是一个 VLAN 接口,数据帧会附带 VLAN ID。
- 内核会处理数据帧,自动加上 VLAN 标签,标记数据帧为 VLAN 22。
- 转发到 WLAN1:
- 当桥接设备 (br0) 将数据帧转发到 WLAN1 接口时,发现 WLAN1 是普通的非 VLAN 接口。
- 内核代码 会检查该数据帧,如果目标接口不支持 VLAN(如 WLAN1),则会 删除 VLAN 标签,并将数据帧作为普通帧传递。
- 删除 VLAN 标签:
- 在数据包的转发过程中,内核通过桥接表和 VLAN 配置,会自动移除 VLAN 标签,完成数据包的转发。
(2)从 wlan0 转发到 eth0.22(无 VLAN 标签的数据转发)
- 数据从 WLAN1 发出:
- 数据帧从 WLAN1 发出时,不带有 VLAN 标签,即普通的以太网帧。
- 当桥接设备 (br0) 收到该帧时,检测到目标接口是 eth0.22(VLAN 接口)。
- 添加 VLAN 标签:
- 内核代码 会自动将 VLAN 标签添加到数据帧,使用 VLAN 22 标记该帧,以便正确地将其传递到 eth0.22。
- 此过程通过 VLAN 驱动程序 实现,该驱动程序负责将 VLAN 标签插入到数据帧中,符合 IEEE 802.1Q 标准。
3. 具体的代码流程
假设我们在桥接中执行 VLAN 标签的添加和删除,相关的代码主要涉及以下几个部分:
(1)VLAN 标签的添加(增加)
在 Linux 内核中,VLAN 标签的添加通常是在数据帧被转发到 VLAN 接口时,由 VLAN 处理模块执行。相关代码可以在
net/bridge/br_vlan.c 和
net/8021q/vlan.c 文件中找到。
函数
vlan_insert_tag():用于将 VLAN 标签插入到数据帧中。
int vlan_insert_tag(struct sk_buff *skb, u16 vlan_id)
{
struct vlan_ethhdr *veth;
struct ethhdr *eth;
eth = eth_hdr(skb);
veth = (struct vlan_ethhdr *)eth;
/* VLAN tag insertion */
veth->h_vlan_proto = htons(ETH_P_8021Q); /* VLAN protocol */
veth->h_vlan_TCI = htons(vlan_id); /* VLAN ID */
skb->protocol = eth_type_trans(skb, skb->dev);
return 0;
}
vlan_insert_tag() 函数会将 VLAN ID 插入到数据帧中,使数据帧符合 IEEE 802.1Q 标准。
(2)VLAN 标签的删除(移除)
当数据包从一个带 VLAN 标签的接口(如 eth0.22)转发到不支持 VLAN 的接口(如 WLAN1)时,内核会通过
vlan_hwaccel_receive_skb() 来删除 VLAN 标签。
函数
vlan_hwaccel_receive_skb():用于处理接收到的带 VLAN 标签的数据帧,并根据目标接口是否支持 VLAN 标签来决定是否删除标签。
int vlan_hwaccel_receive_skb(struct sk_buff *skb, struct net_device *dev)
{
struct vlan_ethhdr *veth = (struct vlan_ethhdr *)eth_hdr(skb);
if (skb_vlan_tag_present(skb)) {
/* Remove VLAN tag */
skb->protocol = eth_type_trans(skb, dev);
skb_pull(skb, VLAN_HLEN); /* Remove the VLAN header */
}
return 0;
}
skb_pull(skb, VLAN_HLEN) 会去除 VLAN 标签(VLAN 头部的长度),确保数据包以普通帧的格式继续传输。
(3)桥接处理的关键代码
在桥接处理中,VLAN 标签的管理通常会在
net/bridge/br_input.c 和
net/bridge/br_forward.c 中完成,涉及到数据包的接收和转发。
函数
br_handle_frame():负责处理桥接设备接收到的帧,并根据目标接口的类型来决定是否需要加或去除 VLAN 标签。
int br_handle_frame(struct sk_buff *skb, struct net_device *dev)
{
struct vlan_ethhdr *veth = (struct vlan_ethhdr *)eth_hdr(skb);
if (dev->vlan_vid) {
/* If the device is VLAN-aware, insert VLAN tag */
vlan_insert_tag(skb, dev->vlan_vid);
} else {
/* If the device is not VLAN-aware, remove VLAN tag */
if (skb_vlan_tag_present(skb)) {
skb_pull(skb, VLAN_HLEN);
}
}
return 0;
}
(4)总结
- VLAN 标签的添加:通过调用类似 vlan_insert_tag() 的函数,数据帧会被标记为特定 VLAN。
- VLAN 标签的删除:在不支持 VLAN 的接口(如 wlan0)上,数据帧的 VLAN 标签会被移除。
- 桥接处理:桥接设备会根据接口的 VLAN 配置自动处理 VLAN 标签的插入和删除,保证数据帧能够正确转发。
这些操作是在 Linux 内核中通过
VLAN 子系统 和
Bridge 驱动程序 完成的,基本上实现了 VLAN 标签的透明处理,无需额外的用户空间配置。