4、VLAN处理:802.1Q标签解析、VLAN成员检查、双标签处理
VLAN处理,说白了就是给以太网帧贴个“门牌号”。
你想想看,一个物理交换机上,怎么让不同部门的电脑互相隔离?靠的就是这个标签。我刚开始接触路由芯片时,觉得VLAN就是个简单的ID号,后来踩了坑才明白——标签的解析、成员关系的检查、双标签的嵌套处理,每一步都可能让转发引擎“翻车”。
4.1 802.1Q标签解析:从比特流里捞出门牌号
标准的802.1Q标签插在源MAC和类型/长度字段之间,一共4个字节。我个人习惯把它的结构拆成三块来看:
- TPID(Tag Protocol Identifier):固定值0x8100,告诉芯片“后面跟着的是VLAN标签”。
- PCP(Priority Code Point):3比特,优先级,用于QoS。
- VID(VLAN Identifier):12比特,取值范围1-4094,0和4095保留。
芯片收到一个帧时,第一步就是检查以太类型字段。如果是0x8100,就说明有单标签。解析逻辑其实很简单:
// 伪代码:单标签解析
if (ether_type == 0x8100) {
vlan_tag = (frame[12] << 24) | (frame[13] << 16) |
(frame[14] << 8) | frame[15];
vid = vlan_tag & 0x0FFF;
pcp = (vlan_tag >> 13) & 0x07;
// 继续解析后面的L2头部
}
嗯,这里要注意:有些老设备会发0x9100或者0x88A8的TPID,那是QinQ或者运营商VLAN的标记。我建议芯片设计时把TPID做成可配置的,别写死。
关键点:解析时一定要考虑“帧偏移”。插入VLAN标签后,MAC地址之后的偏移量从12字节变成了16字节。如果芯片的解析状态机没处理好这个偏移,后面IP头、TCP头的解析全都会错位。
4.2 VLAN成员检查:不是所有端口都能进这个门
解析出VID之后,芯片要干一件很重要的事——检查这个端口是不是该VLAN的成员。
每个VLAN都有一张成员表,记录着哪些端口属于它。我见过不少芯片用位图来实现:一个64位的bitmap,每一位对应一个端口。检查时只需要做一次与运算:
// 伪代码:VLAN成员检查
vlan_member_bitmap = vlan_table[vid]; // 查表得到成员位图
port_bit = 1 << ingress_port; // 入端口对应的位
if (vlan_member_bitmap & port_bit) {
// 端口是成员,继续转发
} else {
// 端口不是成员,丢弃或做特殊处理
}
我曾经在一个项目里遇到过这样的问题:芯片的VLAN表项只有4K条,但客户配置了4096个VLAN。结果VID=0和VID=4095被保留,实际可用只有4094个,但芯片内部表项索引从0开始,导致VID=4095查表时越界。嗯,这个坑后来我们在设计文档里专门加了一条警告。
避坑指南:我曾经因为VLAN成员检查的顺序搞反了,导致广播帧在所有端口都泛洪。正确的顺序应该是:先检查入端口的成员关系,再决定是否接收该帧;然后查MAC地址表决定出端口,最后检查出端口的成员关系。顺序错了,隔离就形同虚设。
4.3 双标签处理:QinQ与运营商VLAN
双标签,也叫QinQ(802.1Q in 802.1Q)。说白了就是在原有VLAN标签外面再套一层标签。外层叫服务标签(S-Tag),内层叫客户标签(C-Tag)。
芯片处理双标签时,解析逻辑要递归一次:
// 伪代码:双标签解析
if (ether_type == 0x8100) {
// 解析第一个标签(可能是S-Tag或C-Tag)
outer_vid = parse_tag(frame, offset);
offset += 4;
if (ether_type_at(offset) == 0x8100) {
// 发现第二个标签
inner_vid = parse_tag(frame, offset);
offset += 4;
// 此时帧是双标签
}
}
我个人习惯把双标签的处理分成三种场景:
| 场景 | 外层标签 | 内层标签 | 芯片处理方式 |
|---|---|---|---|
| 运营商接入 | S-Tag(0x88A8) | C-Tag(0x8100) | 只查S-Tag做转发,C-Tag透传 |
| 企业汇聚 | C-Tag(0x8100) | C-Tag(0x8100) | 两个标签都参与查表 |
| 混合模式 | 可配置TPID | 可配置TPID | 根据配置决定查哪个标签 |
你想想看,如果芯片只支持单标签查找,遇到双标签帧就会把外层标签当成普通数据,转发逻辑全乱套。我记得有一次调试,客户报障说某些VLAN的流量丢了,抓包一看——帧是双标签的,但芯片只解析了外层,内层没处理,导致MAC地址学习错了端口。
设计建议:双标签芯片的查表路径要支持两级查找。第一级用S-Tag查转发域,第二级用C-Tag查具体VLAN。我建议把这两级查找做成流水线,第一级出结果后直接作为第二级查表的索引,这样延迟只增加一个周期。
4.4 标签的添加与剥离:入口和出口的对称操作
VLAN处理不只是解析和检查,还包括标签的添加和剥离。入口芯片可能给帧打上标签,出口芯片可能把标签剥掉。
我总结了一个简单的规则:
- 入口打标签:如果端口配置为Access模式,芯片自动给帧加上PVID对应的VLAN标签。
- 出口剥离标签:如果出端口是Access模式,芯片在转发前把标签剥掉。
- Trunk端口:标签透传,不增不减。
这里有个容易忽略的点:标签的添加和剥离会影响帧的FCS(帧校验序列)。芯片在修改帧内容后,必须重新计算FCS。我曾经见过一款芯片,剥离标签后忘了更新FCS,导致下游设备一直报CRC错误。嗯,这个bug查了整整两天。
核心总结:VLAN处理是路由芯片转发引擎的“门禁系统”。解析标签是识别身份,成员检查是验证权限,双标签处理是应对复杂组网。每一步都做对了,数据才能准确到达目的地。
这张图展示了VLAN处理的核心流程。从接收帧开始,先判断是否有标签,然后解析,接着做成员检查,最后决定是转发还是丢弃。双标签的处理在解析阶段多了一层判断。
个人经验:我建议芯片设计时,把VLAN成员检查的结果缓存下来。因为同一个VLAN的多个帧可能连续到达,缓存可以减少查表的次数。我曾经在一个40G端口的芯片上做过测试,加了缓存后,VLAN处理吞吐量提升了约15%。
好了,VLAN处理这块就聊到这儿。记住三个核心:解析要准、检查要严、双标签要灵活。下次遇到VLAN相关的转发问题,先按这个思路排查,大概率能找到根因。
公众号:蓝海资料掘金营,微信deep3321