4. 数据包分类与标记:从L2到复杂流分类
各位同学,今天我们来聊聊数据包分类与标记。这个话题,说白了就是交换机怎么认出不同的流量,然后给它打上标签。
我刚开始做交换芯片的时候,总觉得分类不就是查查表吗?后来才发现,这里面的门道深着呢。你想想看,一个万兆端口,每秒要处理上千万个包,每个包都要在极短时间内完成分类,这对硬件设计的要求可不低。
4.1 基于L2的分类:802.1p优先级
先说说最简单的——基于二层头的分类。802.1p字段藏在VLAN Tag里,占用3个bit,所以能表示0到7共8个优先级。
802.1p优先级映射表(我常用的配置)
| 802.1p值 | 优先级名称 | 典型应用 |
|---|---|---|
| 0 | BE (Best Effort) | 普通数据 |
| 1 | BK (Background) | 后台流量 |
| 2 | EE (Excellent Effort) | 尽力而为的改进型 |
| 3 | CA (Critical Applications) | 关键应用 |
| 4 | VI (Video) | 视频流 |
| 5 | VO (Voice) | 语音流 |
| 6 | IC (Inter-Network Control) | 网络控制 |
| 7 | NC (Network Control) | 最高优先级 |
802.1p的解析其实很简单。硬件上,我们只需要从报文偏移12字节处(如果是带VLAN的以太网帧)提取3个bit就行。但有个坑——不是所有报文都带VLAN Tag。
我曾经踩过的坑:有一次做一款接入交换机,客户反馈说视频会议卡顿。查了半天,发现是某些终端发出的报文不带VLAN Tag,导致802.1p字段根本不存在,所有流量都被当成优先级0处理。后来我们在入口处加了一个默认优先级映射:不带Tag的报文统一映射到优先级3,问题才解决。
4.2 基于L3的分类:DSCP与IP Precedence
到了三层,事情就变得有意思了。IP头里的ToS字段,早期只用前3个bit做IP Precedence,后来RFC 2474定义了DSCP,用了前6个bit。
我个人习惯把DSCP和802.1p做一个映射表。为什么?因为很多网络设备只认其中一种标记,你需要在入口处做转换。
一个小技巧:在硬件设计时,我建议把DSCP到802.1p的映射做成可编程的。这样客户可以根据自己的需求调整。比如有的客户希望EF(加速转发)对应802.1p 5,有的希望对应7。做成可编程的,一片芯片就能覆盖多种场景。
DSCP的硬件实现,说白了就是一个6bit到3bit的查找表。但要注意,DSCP有64个值,而802.1p只有8个,所以必然存在多个DSCP映射到同一个优先级的情况。
// 典型的DSCP到802.1p映射逻辑(Verilog风格)
always @(posedge clk) begin
case (dscp_value)
6'b000000: prio_out = 3'b000; // Default BE
6'b001010: prio_out = 3'b001; // AF11 -> 优先级1
6'b001100: prio_out = 3'b010; // AF12 -> 优先级2
6'b001110: prio_out = 3'b011; // AF13 -> 优先级3
6'b010010: prio_out = 3'b100; // AF21 -> 优先级4
6'b100010: prio_out = 3'b101; // AF31 -> 优先级5
6'b101110: prio_out = 3'b110; // EF -> 优先级6
6'b111000: prio_out = 3'b111; // CS7 -> 优先级7
default: prio_out = 3'b000;
endcase
end
4.3 基于L4的分类:端口号识别
到了四层,我们开始看TCP/UDP端口号。为什么要看端口号?因为有些应用不按套路出牌——它可能用DSCP标记成普通数据,但实际是视频流。
举个例子:某视频会议软件,它把媒体流标记成DSCP 0(尽力而为),但用的端口是UDP 3478。这时候,光靠L2/L3分类就不够了,必须看端口号。
常见的端口号分类规则:
- HTTP/HTTPS: TCP 80, 443 → 通常映射到中等优先级
- VoIP信令: TCP/UDP 5060, 5061 → 高优先级
- VoIP媒体: UDP 10000-20000 → 最高优先级
- 数据库复制: TCP 1433 (SQL), 3306 (MySQL) → 中高优先级
- P2P下载: 动态端口 → 低优先级或限速
硬件上实现端口号分类,需要做范围匹配。比如UDP 10000-20000,你不能只匹配一个值,得匹配一个区间。我见过有些芯片只支持精确匹配,结果客户想匹配端口范围时,得写几千条规则,TCAM资源直接爆掉。
避坑指南:选芯片时,一定要确认它的L4分类是否支持范围匹配。我曾经在一个项目里,因为芯片不支持端口范围匹配,不得不在上游路由器上做预分类,增加了网络复杂度。
4.4 复杂流分类:ACL的艺术
好了,前面说的都是单层分类。但实际场景中,往往需要组合条件。比如:
- 源IP是192.168.1.0/24
- 目的端口是TCP 80
- DSCP值是0
- 把这些流量标记为高优先级
这就是ACL(访问控制列表)干的事。ACL本质上是一个多字段匹配引擎,它能同时检查L2、L3、L4的多个字段。
硬件实现ACL,主流方案有两种:
| 方案 | 原理 | 优点 | 缺点 |
|---|---|---|---|
| TCAM | 三态内容寻址存储器 | 速度快,单周期完成匹配 | 功耗高,面积大,容量有限 |
| Hash+SRAM | 哈希查找+链表解决冲突 | 功耗低,容量大 | 可能有哈希冲突,延迟不确定 |
我个人更倾向于TCAM方案,虽然贵,但确定性好。在交换芯片里,你没法接受一个报文因为哈希冲突而分类错误。
经验之谈:设计ACL规则时,要注意规则的顺序。TCAM是优先级匹配——排在前面的规则优先级高。我曾经见过一个客户,把一条"deny all"规则放在了最前面,结果所有流量都被丢弃了。嗯,这种问题排查起来特别痛苦。
4.5 分类后的标记与重标记
分类完了,接下来就是标记。标记分为两种:
- 入口标记:在报文进入交换机时,根据分类结果打上内部优先级
- 出口重标记:在报文离开交换机时,修改报文的802.1p或DSCP字段
为什么要做重标记?因为不同网络段的信任策略不一样。比如接入层可能信任802.1p,但核心层信任DSCP。你需要在边界做转换。
我建议在芯片设计时,把标记和重标记做成独立的流水线阶段。这样灵活性更高。有些芯片把标记和分类绑在一起,结果想改标记策略时,得重新配置整个分类表,麻烦得很。
标记策略示例:
// 入口标记逻辑
if (acl_match == VOIP_MEDIA) then
internal_priority = 7
queue_id = 4
else if (acl_match == VIDEO_STREAM) then
internal_priority = 5
queue_id = 3
else
internal_priority = dscp_to_priority(dscp_field)
queue_id = internal_priority
end if
// 出口重标记逻辑
if (egress_trust_mode == TRUST_INTERNAL) then
dscp_out = priority_to_dscp(internal_priority)
dot1p_out = priority_to_dot1p(internal_priority)
else if (egress_trust_mode == TRUST_REMARK) then
dscp_out = remark_table[internal_priority]
dot1p_out = remark_table[internal_priority]
end if
4.6 知识体系总览
说了这么多,我画了一张图,把整个分类与标记的流程串起来。你一看就明白了。
这张图把整个流程串起来了。报文进来后,先经过分类阶段,检查L2、L3、L4和ACL规则。如果匹配到ACL,就用ACL指定的优先级;否则走默认映射。最后进入标记阶段,打上内部优先级,出口时还可以做重标记。
好了,数据包分类与标记的内容就讲到这里。记住一句话:分类是QoS的基础,分类做不好,后面的调度、整形都是白搭。下一节我们聊聊队列管理与拥塞避免,到时候见。
公众号:蓝海资料掘金营,微信deep3321