视角:配置器(手机 App)日志 · 依据:配置器日志 + 中枢侧日志 + iotc 源码三方交叉核实
0 · 一句话概括
配网全程 11 步,两个平面:
BLE GATT 平面(步骤 1~8)
手机与设备通过蓝牙完成发现、建链、SPEKE 认证、PSK 下发、WiFi 凭证下发。步骤 3(SPEKE msg4)之后 payload 全程加密。
UDP / CoAP 平面(步骤 9~11)
设备拿到 IP 后不再走蓝牙,改为 UDP 广播上线通告,手机再与中枢做 HubSync 与设备管理信息同步。
介质切换点在步骤 8 → 9 之间:netCfg 100 是 BLE 平面最后一个事件,online_notify 是 UDP 平面第一个事件,二者由同一 CONNECTED / NET_CONNECT 事件平行驱动,无严格先后(实证:18:06:46.123 先收到 notify,.146 才报 100)。
1 · 总览表
| 步 | 阶段名 | 平面 | 方向 | 关键报文 | 要点 |
|---|
| 1 | 蓝牙广播扫描 | BLE | 设备 → 手机 | ADV(含 softApSsid) | 拿到设备标识与协议类型 |
| 2 | BLE 连接 / GATT 发现 | BLE | 双向 | GATT 15F1E600-… | 按 service-name 路由 |
| 3 | SPEKE 协商 | BLE | 双向 | msg1→msg2→msg3→msg4 | 此后 payload 全程加密;对重传敏感 |
| 4 | deviceInfo | BLE | 手机 → 设备 | prodId / udid / 协议版本 | 决定后续协议分支 |
| 5 | getProvisionRequest | BLE | 手机 → 设备 | pskAuth 参数 | 取认证参数 |
| 6 | provisionPSK | BLE | 手机 → 设备 | pskId + psk | 手机下发;先 PSK 后 WiFi |
| 7 | hubCfg | BLE | 手机 → 设备 | ssid / pwd / bssid / channel | 真实 WiFi 凭证下发 |
| 8 | netCfg 上报 | BLE | 设备 → 手机 | 104 → 100 | 100 是提前上报,非连通证明 |
| 9 | online_notify | UDP | 设备 → 手机 | 255.255.255.255:5683 | 非请求广播;真正的入网判据 |
| 10 | HubSync | UDP | 手机 → 中枢 | SEARCH → DTLS 5670 | 三段式,使能本地中枢 |
| 11 | synvDevManageInfo | UDP | 手机 → 中枢 | POST /hub/synvDevManageInfo | 同步设备管理信息 |
2 · BLE GATT 平面(步骤 1~8)
此后 payload 加密手机(配置器)设备1. ADV 广播(含 softApSsid)2. BLE 连接 · GATT 15F1E6003. SPEKE msg1→msg4(X25519)4. deviceInfo:prodId/udid/协议版本5. getProvisionRequest:pskAuth 参数6. provisionPSK:pskId+psk7. hubCfg:ssid/pwd/bssid/channel8. netCfg 上报:104→100
BLE GATT 平面:步骤 1~8,SPEKE 握手完成后进入加密区
步骤 1 · 蓝牙广播扫描
| 方向 | 设备 → 手机(广播) |
| 关键字段 | softApSsid、prodId、protocol、udid |
| 日志关键词 | CoapDeviceScanManager、diag scanItem prodId=... protocol=... udid=... |
| 易错点 | 配置器侧 stopScanDevice 不等 binder 完成就 startScan → BLE binder busy → 等 60s → superseded 死循环,扫描根本起不来 |
步骤 2 · BLE 连接 + GATT 服务发现
| 方向 | 双向 |
| 服务 UUID | 15F1E600-A277-43FC-A484-DD39EF8A9100 |
| 路由方式 | 按 service-name 分发(g_svcInfoTab),不走 ServiceProxy |
| 后续 | 订阅配网特征值(CCCD 写 0x0001),进入 SPEKE |
步骤 3 · SPEKE 协商(最脆弱的一步)
| 方向 | 双向,四步 msg1 → msg2 → msg3 → msg4 |
| 算法 | SPEKE/v2,X25519;iterations 为关键参数 |
| 完成标志 | PakeTask: verifyProof 命中 → PakeTaskStatus 65535(终态);正常路径 32769 → 32770 → 65535 |
| 此后 | 所有 payload 加密,日志只剩 cmdType 与长度 |
实证过的失败模式:重传定时器竞态
- 设备侧
iterations=10000 时 msg1→msg2 耗时 1561ms(1000 时为 143ms) - 手机侧 PAKE 重传定时器 1500ms → msg1 被重传 → 设备对两条各回一条 msg2(逐字节相同)
- 手机状态机被重入两次
sendClientConfirm → 两个不一致的 kcfData2 → 设备拒第一条:{"errCode":-196548} -196548 = IOTC_CORE_COMM_SEC_ERR_SPEKE_VERIFY_HMAC(module=3,base -196608,index 60)- 手机已推进到终态 65535,
spekeMsg4 被当作 insignificant passThrough data 丢弃 → 12.6s 超时 → speke fail. - 源码:
security_speke_nego_ctx.c:357-374(NegoContextVerifyHmac)、security_speke_server.c:99 - 判据差异:走 CoAP 传输时 libcoap 按 token/MID 天然去重,同样
iterations=10000 也能成功;走 BLE GATT 无去重,重传必然重复回调 - 修复方向:
iterations 回退 1000;重传超时 ≥3000ms;sendClientConfirm 按 sessionId+msgId 幂等
步骤 4 · deviceInfo
| 方向 | 手机 → 设备(加密) |
| 取回字段 | prodId / udid / deviceModel / scannedProtocol / protocolVersion |
| 说明 | 常被忽略,但它是后面选择协议分支(BLE 协议 / softap 协议)的依据 |
| 补充事实 | queryDeviceInfo 回包里没有 devId,devId 由手机侧生成并在步骤 7 下发;每轮 devId 不同属正常 |
步骤 5 · getProvisionRequest
| 方向 | 手机 → 设备(加密) |
| 请求 | payloadLength = 266 |
| 回包 | {"code":0,"version":"2.0","cipherSuite":1,"pskAuth":{sessionId,vid,pid,pskId,...}} |
| 日志关键词 | deviceAuth success、pskAuth |
步骤 6 · provisionPSK
| 方向 | 手机下发给设备(不是设备回给手机) |
| 报文 | {"domainId","pskId":"4225","psk":"97F7E8...","valid":...} |
| 设备回包 | {"code":0},共 10 字节 |
| 时机 | 先 PSK,后 WiFi 配置(步骤 6 在步骤 7 之前) |
陷阱:两套 PSK 体系不能混为一谈。
provisionPSK 下发的是配网用 PSK;DTLS initLocalHub 用的是另一把(来自 ConfiguratorCredentialStore,identity A)。排查时以 pskId 来源区分。
步骤 7 · hubCfg
| 方向 | 手机 → 设备(加密) |
| 字段 | ssid / password / bssid / channel / devId / code |
| 日志关键词 | hubConfig success |
| 注意 | 设备侧 CopyWifiResultFromOhos 只比 SSID + 加密类型,不比 bssid;IsWifiConnectedToTarget 也只比 ssid。bssid 下发错了不会阻塞连接 |
步骤 8 · netCfg 上报 104 → 100
| 方向 | 设备 → 手机(加密) |
| 状态码 | 104 = connecting,100 = IOTC_CORE_WIFI_EVENT_CONNECTED |
| 陷阱 | 100 不等于拿到 IP:svc_conn_action.c:78-101 在 IotcConnectWifi() 返回后立刻 publish CONNECTED。实证假阳性:104→100 只隔 8ms(正常轮次 3.9~5.1s) |
| 触发链 | wifi_service_adapter.c:32/44(.eventNetConnect)→ ble_svc_netcfg_status.c:104-108:报 100 时主动补发一次 IOTC_SDK_AILIFE_EVENT_WIFI_NET_CONNECT → 驱动 lan_search 起 coap → 发 online_notify |
| 真正判据 | 连通性判定在 60s WIFI_VERIFY_CONNECT_TIMEOUT_MS 里,失败才发 102 |
3 · UDP / CoAP 平面(步骤 9~11)
设备手机(配置器)中枢9. online_notify 广播255.255.255.255:5683 · 非请求广播 · 配置器 waitOnline 判据10. HubSync:SEARCH → DTLS 5670HUB_SEARCH 组播 → HUB_IDENTITY → initLocalHub(DTLS)11. POST /hub/synvDevManageInfoDTLS CoAP · 依赖步骤 10 成功
UDP / CoAP 平面:步骤 9~11,蓝牙不再参与
步骤 9 · online_notify
| 方向 | 设备 → 手机,受限广播 255.255.255.255:5683 → 手机 0.0.0.0:5683 |
| 性质 | 非请求主动上报(不是某个请求的响应) |
| 源码 | net_lan_search_coap_api.c:124-215,SEND_COUNT=5、INTERVAL=1s |
| 双受众 | ①配置器:waitOnline 60s 窗口,onlinenotifyRx=1 → onlineJudge → result=true,这是 App 判定"配网成功"的端到端证据;②中枢:同一广播发现新设备,驱动后续 HubSync |
4 个静默跳过点(命中即"零发包",日志里什么都看不到)
net_lan_search_coap_api.c:88-93:ConfigGetDevId 失败/空 → 只打 online notify devId invalidnet_lan_search_svc.c:77-79:!enableOnlineNotify → 直接 return,无任何日志NetLanSearchCoapStackStart 失败 → start lan search coap server error %d- 5 次发送全落在"还没拿到 IP"的窗口 →
send onlinenotify error %d
工程实践建议(基于实测)
100 的上报应以"真实连通"为门控(目标 SSID 已关联 + IP 已就绪)后再发一次,而不是在连接 API 返回时立刻发- online_notify 发送前先判本地 IP 非
0.0.0.0,未就绪不消耗重发次数 - 重发策略改为"先快后慢"(如 5×1s 快发后降频 5s,总时长覆盖配置器 60s 等待窗口)
- 同时发受限广播
255.255.255.255 与子网定向广播,避免只依赖一种
步骤 10 · HubSync
三段式:
- HUB_SEARCH:组播
239.255.71.2:5685,5 次 × 3s,超时 → {"found":false}(带 preferHash 仍会继续走下一步) - HUB_IDENTITY:
HUB_IDENTITY skip cloud verify (lab flag) —— 设计行为,不是失败 - initLocalHub:DTLS
中枢IP:5670,DTLS13 3s 超时 → 回退 DTLS12 30s,仍 udpReadable=0 → 33s 后 {"mCode":268369917} = 0xF0010003
| 现象 | 判据 | 排查方向 |
|---|
| HUB_SEARCH 5 次全超时 | {"found":false} | ① 中枢侧 socket 是否 bind 且已加入组播组(bind 0.0.0.0:5685 ≠ 能收组播,必须 IP_ADD_MEMBERSHIP);② 配置器组播出口网卡是否选对(多网卡时默认路由可能走错接口);③ 中枢业务状态机未就绪时 handler 会早退不回包 |
| initLocalHub DTLS 零回包 | udpReadable=0 持续到超时 | ① DTLS server 根本没收包:看 socket inode 创建时机、是否挂进事件循环;② 收到了但静默丢弃:PSK/credential 未装载(PSK 模式找不到 identity 时很多实现不回 alert);③ udpReadable=0 只是本端没收包,不能证明对端没收到 |
| DTLS13 失败 | 3s 超时后回退 DTLS12 | 属兼容路径,回退成功即可用;两边版本能力需对齐 |
errCode 268369917 | = 0xF0010003 | 本地中枢使能失败,可重试 |
认知要点:WiFi 配网流程与中枢状态机是两套独立的东西。BLE/SPEKE/hubCfg/netCfg/online_notify 全部走通,也不代表中枢已进入可服务状态——步骤 10/11 的前置条件是中枢自身的业务状态机已 READY。
步骤 11 · synvDevManageInfo
| 方向 | 手机 → 中枢 |
| 报文 | DTLS CoAP POST /hub/synvDevManageInfo |
| 拼写 | 源码里是 synv(不是 sync),grep 时别写错 |
| 成功标志 | synvDevManageInfo errCode=0 → 配置器侧 netConfig result = true,整条配网链路结束 |
| 合法跳过 | 待配设备不是中枢时:HUB_ENABLE skip not hub-center → state=HUB_SKIP provisionOk=true,是设计行为,不是失败 |
| 依赖 | 以步骤 10 的 initLocalHub 成功为前提;HubSync 失败则 HUB_SYNC skip,本报文不会执行 |
4 · 关键陷阱清单
| # | 陷阱 | 后果 | 判据 |
|---|
| 1 | SPEKE iterations 过大 + BLE 无去重 + 1500ms 重传 | msg2 重复处理,-196548 HMAC 校验失败 | PakeTask: SPEKE3.0 iteration 出现 2 次 |
| 2 | netCfg 100 是提前上报 | 假阳性,8ms 就报 100 | 104→100 间隔 < 1s 即可疑 |
| 3 | 两套 PSK(provisionPSK vs DTLS credential) | 排查时张冠李戴 | pskId 是否来自 ConfiguratorCredentialStore |
| 4 | online_notify 4 个静默跳过点 | 60s 超时,日志零线索 | onlinenotifyRx=0 coapRxAny=false |
| 5 | 中枢业务状态机未 READY(如停在 UNINIT) | 步骤 10/11 全挂,且 WiFi 侧全通也无济于事 | 中枢日志 handler 进入后即返回、无 tx |
| 6 | synv 拼写 | grep 搜不到 | — |
| 7 | HUB_SKIP / HUB_IDENTITY skip | 误判为失败 | 看 provisionOk=true 即可确认配网本体成功 |
5 · 排障速查表
| 日志关键词 | 所处步骤 | 常见结论 |
|---|
BLE binder busy / superseded session | 1 | 扫描没起来,配网根本没开始 |
speke fail. / errCode:-196548 | 3 | iterations + 重传竞态 |
verifyProof 0 次 | 3 | 密钥确认没通过 |
deviceAuth success | 5-6 | PSK 认证已过 |
netCfg 104 后无 100 | 8 | WiFi 关联失败 |
104→100 间隔 < 1s | 8 | 假阳性,看 notify 才准 |
onlinenotifyRx=0 + timer timeout | 9 | 设备没真入网 / notify 被静默跳过 |
HUB_SEARCH ... found=false | 10a | 组播没入组 / 出口网卡错 / 中枢未就绪 |
initLocalHub ... 268369917 | 10c | DTLS 零回包,查 socket 与 PSK 凭证装载 |
HUB_SYNC skip | 11 | 前置 HubSync 失败,或被设计跳过 |
6 · 小结:理解这套流程的三个关键
- 两个平面的切换点在步骤 8→9。BLE 只负责"把 WiFi 凭证安全地送进去",设备一入网,后续所有通信(配置器↔设备、配置器↔中枢)全部走 UDP/CoAP,蓝牙不再参与。
- 加密从步骤 3 结束后开始。SPEKE msg4 之后 payload 全程加密,所以步骤 4~8 的日志只能看到命令名和长度,看不到内容——这是判断"卡在认证前还是认证后"的自然分界。
netCfg 100 不等于联网成功。真正端到端的入网证明是步骤 9 的 online_notify:设备真的拿到了 IP 并且局域网可达。这是配置器 waitOnline 的唯一可信判据。
所有评论(0)