Skip to content

QoS 标记:DSCP 是怎么被读、存、写的

QoS 的第一步永远是分类与标记 —— 给每个包贴上"你有多重要"的标签, 后面的排队、调度、丢弃策略才有依据。

这一篇讲 VPP 怎么处理这个标签,以及一个 CLI 上做不到的事。

标签在哪

IPv4 头里的第二个字节,历史上叫 TOS,现在拆成两部分:

text
        IPv4 头的第 2 个字节(8 bit)
  ┌─────────────────────────────┬───────┐
  │        DSCP  (6 bit)         │ ECN(2)│
  └─────────────────────────────┴───────┘
     区分服务代码点                 显式拥塞通知

  TOS = DSCP << 2 | ECN

常用的 DSCP 值:

名称十进制用途
BE (CS0)0尽力而为,默认
AF1110低优先级业务
AF2118
AF3126
AF4134视频
EF46语音,最低时延
CS648网络控制协议(路由协议)

怎么自己观察 DSCP

本教程用一个几十行的 Python 工具读取收到报文的 TOS 字节:

python
sock.setsockopt(socket.IPPROTO_IP, socket.IP_RECVTOS, 1)
_, ancdata, _, peer = sock.recvmsg(2048, socket.CMSG_SPACE(1))
for level, ctype, data in ancdata:
    if level == socket.IPPROTO_IP and ctype == socket.IP_TOS:
        tos = data[0]

有个容易踩的细节:IP_RECVTOS 是"打开这个功能"的开关, 内核实际交上来的辅助数据类型是 IP_TOS 两者写混了永远读不到值 —— 我第一次写就错在这里,收到包但 TOS 一直是 None。

VPP 的三段式模型

VPP 把 QoS 处理拆成三个独立动作:

拆开的好处是灵活:可以从 VLAN 优先级读、写到 IP DSCP 上, 或者从 MPLS EXP 读、写到 VLAN 上 —— 跨封装层翻译 QoS 标记。

bash
qos record <source> <> [disable]      # 入向读
qos store  <source> <> [disable]      # 存元数据
qos mark   <source> <> id <MAP>       # 出向写
qos egress map id <N> {...}               # 映射表

<source> 可以是 ip / mpls / vlan / ext

qos record 确认可用:

bash
vppctl qos record ip host-eth1
vppctl show qos record
text
 host-eth1:
  IP

挂上之后 feature arc 里会出现 ip4-qos-record

一个 CLI 上做不到的事

qos egress map 的 CLI 在 26.06 上无法使用

映射表是三段式的核心 —— 没有它,qos mark 无从谈起。 但它的 CLI 语法怎么都试不对。

帮助文本本身就有问题(%d 是个没被填充的格式串):

text
qos egress map    qos egress map id %d [delete] {[SOURCE][INPUT]=OUTPUT}
                                    ^^ 未展开的格式符

试过的所有写法:

text
  ip 46=10          → ip: unknown input `46=10'
  ip 46 = 10        → ip: unknown input `46 = 10'
  ip [46]=10        → ip: unknown input `[46]=10'
  {ip 46=10}        → unknown input `{ip 46=10}'
  ip 0x2e=0x0a      → ip: unknown input `0x2e=0x0a'
  ip 46,10          → ip: unknown input `46,10'
  ip 46:10          → ip: unknown input `46:10'

注意报错形式:ip: 这个前缀说明 ip 已经被正确解析了, 失败的是后面那部分。但没有任何一种写法能通过。

映射表能创建(show qos egress map id 1 有输出),但全是 0, 没办法往里填值:

text
 Map-ID:1
  IP:[0,0,0,0,0,0,0,0,...]     ← 256 个全零

结论:QoS 映射表这条路在 26.06 的 CLI 上走不通。 要用的话得走 VPP API(binary API 里有 qos_egress_map_update), 或者用下面的替代方案。

替代方案:用 Policer 做标记

好消息是标记 DSCP 并不一定要走 qos mark。 Policer 的 mark-and-transmit 动作可以直接写 DSCP,而且它工作正常:

bash
vppctl policer add name p type 2r3c-2698 \
    cir 200 cb 200 eir 600 eb 600 rate pps \
    conform-action mark-and-transmit AF11 \
    exceed-action  mark-and-transmit CS1 \
    violate-action drop
vppctl policer input name p host-eth1

实测端到端有效:

text
  h1 发出          DSCP=46 (EF)
  s1 收到          117 个 DSCP=10 (AF11)
                   533 个 DSCP=8  (CS1)

这比纯粹的静态映射更实用 —— 标记依据是实际用量而不是客户自己的声明。 下一篇专门讲 Policer。

DSCP 必须用大写名称

bash
conform-action mark-and-transmit AF11
conform-action mark-and-transmit af11 unknown input
conform-action mark-and-transmit 10 unknown input

小写和数字都会被拒绝,而且报错信息是被截断的 policer add: unknown input 'conform-action mark-and-transm...', 看不出到底是哪个参数有问题。

信任边界:为什么要重新标记

一个实践问题:客户发来的包已经带了 DSCP,该信吗?

答案通常是不信。任何人都可以把自己的包标成 EF(最高优先级), 如果网络照单全收,QoS 体系立刻失效 —— 所有人都会把自己标成最高。

标准做法是在信任边界上重新标记:

text
  边界之外(不可信)   客户随便标,标了也不算
  边界之上(重标记)   按合同速率和实际用量决定真实优先级
  边界之内(可信)     后续设备信任这个标记,按它调度

实验里那个 policer 就是在做这件事:客户标 EF, 但只有 cir 以内的流量拿到 AF11,超出部分降到 CS1,再超直接丢。

VPP 不做的部分

QoS 的完整链条里,VPP 这一层只管标记

text
  分类 → 标记 → 排队 → 调度 → 拥塞管理
  ─────────────  ────────────────────
   VPP 覆盖       主要靠网卡硬件队列 / 下游设备

VPP 的 QoS 是标记和限速,不是排队调度。真正的按优先级排队、 加权公平队列(WFQ)、低时延队列(LLQ)这些,在 VPP 里没有对应的通用实现 —— 它们通常由网卡的多队列 + DPDK 的 traffic manager,或者下游的物理交换机来做。

所以 VPP 在 QoS 体系里的定位是边界标记设备: 把流量分好类、打好标、限好速,交给下游按标签调度。

小结

标签位置IPv4 头第 2 字节:DSCP(6bit) + ECN(2bit),TOS = DSCP<<2
VPP 模型三段式:qos record(读)→ qos store(存)→ qos mark(写)
可用的qos record ip <接口>
不可用的qos egress map 的 CLI 在 26.06 上无法填值,需走 API
实用替代Policer 的 mark-and-transmit,DSCP 用大写名称
观察方法IP_RECVTOS 开关 + IP_TOS 辅助数据(别写混)
VPP 的边界只管标记和限速,排队调度靠网卡和下游设备

下一篇:Policer 限速与三色标记

实验环境基于 netlab + containerlab + VPP 26.06