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处理核心流程 接收以太网帧 检查以太类型字段 0x8100? 解析单标签 提取VID/PCP 双标签? VLAN成员检查 查成员位图,确认端口权限 通过? 转发处理 查MAC表,决定出端口 丢弃帧 标签 添加/剥离

这张图展示了VLAN处理的核心流程。从接收帧开始,先判断是否有标签,然后解析,接着做成员检查,最后决定是转发还是丢弃。双标签的处理在解析阶段多了一层判断。

个人经验:我建议芯片设计时,把VLAN成员检查的结果缓存下来。因为同一个VLAN的多个帧可能连续到达,缓存可以减少查表的次数。我曾经在一个40G端口的芯片上做过测试,加了缓存后,VLAN处理吞吐量提升了约15%。

好了,VLAN处理这块就聊到这儿。记住三个核心:解析要准、检查要严、双标签要灵活。下次遇到VLAN相关的转发问题,先按这个思路排查,大概率能找到根因。


公众号:蓝海资料掘金营,微信deep3321