主题
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 | 尽力而为,默认 |
AF11 | 10 | 低优先级业务 |
AF21 | 18 | |
AF31 | 26 | |
AF41 | 34 | 视频 |
EF | 46 | 语音,最低时延 |
CS6 | 48 | 网络控制协议(路由协议) |
怎么自己观察 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 recordtext
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 限速与三色标记。