Files
vd_960/vd960DBN/docs/incidents/2026-07-15-DC045A49718F-protocol-error.md
T
wangfq e3eaa111fe fix(mqtt): 修复 loop_data>512B 溢出发垃圾包致 broker RST 重连风暴 (现场P0)
根因(非平台推测的环形缓冲/event_report队列):
  mqtt_publish 的 mqttBuf=512B < loop_data(4通道)604B
  → MQTTSerialize_publish 返回 -2 不写 buf
  → len 为 uint32_t, -2 变 42.9亿
  → WCHNET 把清零缓冲+越界相邻全局(temp_guide=report_config→report_c)
    当 520B 垃圾包发出 → broker 见非法类型0x00 RST → 重连风暴

修复(net_srv.c):
  - MAX_MQTTBUF_LEN 512→1024 (容纳 604B loop_data)
  - mqtt_publish: len uint32→int + 守卫 if(len<=0)return (序列化失败绝不发残缓冲)
  - keepalive 9→60 (报告建议, 9s过激)

event_report 协议违规修复(iot_mqtt_srv.c):
  - 重连重发保持原 msg_id/ts (V1.04 §5.3-2), 不再作废换号致平台去重失效

验证: tests/test_mqtt_publish_overflow.c 复现旧垃圾包+验证守卫/大缓冲;
      tests/test_event_report.c T8 断言重连同 msg_id. 均全过.
遗留: ts=上电秒数非Unix时间戳, 待定方案(设备无RTC/SNTP)

Refs: docs/incidents/2026-07-15-DC045A49718F-protocol-error.md
2026-07-15 14:58:03 +08:00

2.5 KiB
Raw Blame History

抓包存档:DC045A49718F MQTT protocol error 风暴

  • 文件: 2026-07-15-DC045A49718F-protocol-error.pcap44 包,20s 窗口)
  • 抓包时间: 2026-07-15 14:26 左右(设备 14:20 上电后持续复现)
  • 抓包点: 腾讯云 159.75.137.141tcpdump -i any port 1883 and host 113.91.145.40
  • 设备: DC045A49718F (DLD960GA, hard 1.0 / soft 1.0,新版 event_report 重发固件)

现象

设备每 ~1s 被 mosquitto 以 disconnected due to protocol error 踢下线, 15 分钟内 80+ 次重连、87 次 initialize 上报。

pcap 关键证据(用 tcpdump -r <pcap> -nn -ttt -X 查看)

正常序列先走完:

CONNECT (MQTT 3.1.1, client_id=DC045A49718F, u=admin, keepalive=9)
→ CONNACK
→ SUBSCRIBE dld960/DC045A49718F/srv → SUBACK
→ PUBLISH dld960/DC045A49718F/dev {"cmd":"initialize", msg_id:21, ts:393, ...}   ← 报文完好

紧接着设备发出致命包:

TCP 段 520 字节 = 前 512 字节全 0x00 + 尾部 8 字节 ASCII "report_c"
  • 0x00 不是合法 MQTT 控制报文类型(type 0 保留)→ broker 立即 RST 断连
  • RST 后设备仍继续发第二个 520B 全零段(seq 805:1325

诊断

520 = 512 + 8 → 高度疑似 512B 环形 TX 缓冲区回绕(wrap)处理 bug。

推测:往 TX 缓冲写 report_config 响应时跨越回绕边界,"report_c" 落在缓冲区 物理尾部,其余部分绕回头部;发送侧却把整块未初始化(全零)缓冲区一次性发出。 本次固件恰好新增了事件待发队列 + 5s 重发机制(V1.04),动过 TX 路径, 建议重点排查:

  1. 新待发队列的写指针 / 回绕边界处理
  2. 重发定时器与主循环并发 publish 的互斥(两个写者交叉写 TX 流)
  3. 发送长度是否误用缓冲区总长而非报文实际长度

顺带实锤的协议违规(固件侧待修)

  1. 断线重连后重发事件用了新 msg_idmsg_id 9/10 内容完全相同 (car_leave ch1 value=48, ts=76),双双入库 → 平台 (dev_serial,msg_id) 去重失效。V1.04 §5.3 条 2:重发必须用相同 msg_id(跨重连也要保持)
  2. ts 字段是上电秒数(76/393),不是协议约定的 Unix 时间戳
  3. keepalive=9s 过于激进(老问题),建议 30~60s

平台侧结论(无需改动)

event_report 必答闭环工作正常:ack <1s、回显 msg_id、先落库后应答; msg_id 相同的重复包去重有效。重复入库仅发生在固件违规换 msg_id 的场景。