欢迎光临
我们一直在努力

EASYMESH-02-ieee1905-1-协议详解

IEEE 1905.1 协议详解(阶段 1 笔记)

学习目标:搞懂 EasyMesh 所有上层消息赖以传输的底层协议——IEEE 1905.1 汇聚层。 方法:协议原理为主线,prplMesh 源码作佐证(代码只用来印证\”协议字段长这样、怎么处理\”)。 代码路径:prplMesh/framework/transport/ieee1905_transport/、prplMesh/framework/tlvf/


0. 一句话理解 IEEE 1905.1

IEEE 1905.1 是一个\”汇聚层\”(Convergence Layer)协议:它在 Ethernet、Wi-Fi、Coax、PLC 等多种异构底层链路之上,抽象出一套统一的控制面消息机制,让多台设备能在一个\”逻辑网络\”里互相发现、交换拓扑、协同管理。

EasyMesh 并不是直接跑在 Wi-Fi 上的协议——EasyMesh 的所有消息(Topology Discovery、AP Auto-Config、Steering…)都封装在 1905.1 CMDU 里,再由 1905.1 汇聚层通过以太网/Wi-Fi 等链路传输。

协议位置示意:

应用层:EasyMesh(Controller/Agent 业务逻辑)
↓ 封装为
汇聚层:IEEE 1905.1(CMDU + TLV) ← 本笔记讲这一层
↓ 承载于
链路层:Ethernet / Wi-Fi / Coax / PLC


1. 核心概念

1.1 AL MAC(AL = Abstraction Layer)

每个 1905.1 设备有一个 AL MAC 地址,它是该设备在 1905.1 逻辑网络里的\”门牌号\”。注意:

  • AL MAC 不等于任何一个物理网卡的 MAC,它是本机 1905.1 汇聚层(Abstraction Layer) 的逻辑地址——不是 Controller 分配的
  • 这里的「汇聚层」= IEEE 1905.1 协议本身那一层(Convergence / Abstraction Layer),不是 EasyMesh 的 Controller 角色
  • 每个设备自己都有汇聚层,所以每台跑 1905.1 的设备(无论 Controller 还是 Agent)都有自己的一个 AL MAC
  • 一个设备只有一个 AL MAC,但可能有多个底层接口(多个以太网口、多个 Wi-Fi radio)
  • EasyMesh 通信寻址用的是 AL MAC,不是底层接口 MAC
  • 实现上 AL MAC 常取 bridge 口 MAC 或某个主接口 MAC(看起来“像”物理 MAC),但语义上它代表的是整台设备的 1905 逻辑身份,不是“某一个网卡”

ALID(AL Identifier):通常与 AL MAC 一致,用于唯一标识一个 1905.1 实体。

┌─ 设备 A(主路由,Controller+Agent)─┐
│ AL MAC = AA:AA:AA:AA:AA:AA ← 整机门牌号(1 个)
│ eth0 MAC / wlan0 MAC / wlan1 MAC … ← 多个物理口 MAC
└────────────────────────────────────┘

┌─ 设备 B(扩展器,纯 Agent)─────────┐
│ AL MAC = BB:BB:BB:BB:BB:BB ← 自己的门牌号(自己的汇聚层)
│ eth0 MAC / wlan0 MAC …
└────────────────────────────────────┘

Controller 用 AL MAC 跟 Agent 说话:发往 BB:BB:…,不是发给某个 radio 口 MAC。

1.2 多链路抽象

1905.1 的精髓:屏蔽底层差异。对上层而言,\”发一条消息给邻居 AL MAC\”就够了,不用关心这条消息实际走了以太网还是 Wi-Fi。汇聚层负责:

  • 在每个底层接口上收发 CMDU
  • 维护\”AL MAC ↔ 哪个接口可达\”的映射
  • 跨链路转发(一条消息从 Wi-Fi 进来,可能从以太网转出去)

1.3 多播地址

1905.1 用一个固定的组播 MAC 地址做广播发现:

01:80:C2:00:00:13

所有 1905.1 控制消息(如 Topology Discovery)都发到这个组播地址。注意 IEEE 802.1D 的严格保留组播段是 01:80:C2:00:00:00 ~ 01:80:C2:00:00:0F(最后字节 0x00~0x0F,即 0~15),网桥对这一段不转发;而 1905.1 用的是 0x13(=19),已超出 0x00~0x0F 保留段,所以 Linux bridge 默认会把它当普通组播泛洪到所有端口。这会破坏 1905.1 自己的 relay 转发机制(见第 7 节),因此必须用 ebtables 规则阻止 bridge 泛洪,把转发权交给协议层。

代码佐证:prplMesh 把这个地址定义在头文件里

// ieee1905_transport.h
static constexpr sMacAddr ieee1905_multicast_addr = {


.oct = {

0x01, 0x80, 0xc2, 0x00, 0x00, 0x13}};

并在注释里提醒:IEEE1905 组播包不应被 Linux 网桥泛洪,需要打 ebtables 规则屏蔽:

ebtables -t filter -I FORWARD 1 -p 0x893a –destination 01:80:c2:00:00:13 -j DROP

规则拆解:

  • -t filter:操作 filter 表(网桥转发过滤的默认表)
  • -I FORWARD 1:在 FORWARD 链最前面插入规则(最高优先级)。FORWARD 链管的是\”bridge 从一个口转发到另一个口\”的包
  • -p 0x893a:匹配 EtherType=0x893a 的帧(1905.1 CMDU)
  • –destination 01:80:c2:00:00:13:匹配目的 MAC = 1905.1 组播地址
  • -j DROP:命中即丢弃,不让 bridge 跨口转发

为什么要这条规则——分清两种\”转发\”:

  • Linux bridge 的泛洪转发(要禁止):bridge 收到组播包默认从所有其他口发出去,是无脑复制,不可控。
  • 1905.1 协议层的 relay 转发(要保留):靠 CMDU Header 的 RelayIndicator 位精确控制——仅对组播 01:80:C2:00:00:13 有效:relay=1 才跨多跳中继,relay=0 只上交本机、不外传中继(单播不受 relay 影响,见第 7.4 节)。
  • 如果让 bridge 先泛洪,协议层的 relay 机制就形同虚设,还会产生重复包风暴。这条规则的作用是把转发权从 bridge 手里夺过来交给协议层:bridge 只负责把包上交本机(走本机接收路径),不负责跨口转发(FORWARD 链被 DROP)。

    比方:假设路由器上 br0 桥接了 eth0(有线口)和 wlan0(Wi-Fi 口)。没有这条规则时,Wi-Fi 口收到的 1905.1 组播包会被 bridge 无脑复制到 eth0 也发一份(泛洪);打了这条规则后,Wi-Fi 口收上来的包不再被 bridge 转发到 eth0,而是只上交给本机 1905.1 协议层处理——由协议层看 relay 位决定要不要从 eth0 再发出去。即\”bridge 不掺和转发,转发归协议层管\”。

    完整对比(br0 桥接 eth0 + wlan0,wlan0 收到 1905.1 组播包):

    没有 ebtables 规则: 有 ebtables 规则:
    wlan0 收到 1905.1 组播包 wlan0 收到 1905.1 组播包
    → bridge 泛洪:eth0 也收到一份 ❌ → bridge FORWARD 命中 DROP:不复制到 eth0 ✓
    → 本机协议层也收到一份 → 本机协议层收到一份
    → 协议层 relay=1 又从 eth0 发一次 → 协议层看 relay=1,自己从 eth0 精确发一次
    → eth0 收到 2 份,重复 ❌ → eth0 收到 1 份,干净受控 ✓

    两个易错细节澄清:

  • 不是\”直接丢给内核协议栈\”:bridge DROP 掉 FORWARD 链后,包并没有被完全丢弃,而是仍然会上交本机(本机接收路径)。本机的 1905.1 协议层(prplMesh 里是用户态进程)收到后再决定怎么处理。
  • \”不转发到其他口\”是对的,但协议层可能会自己再发:bridge 不转发 ≠ 包就不出去了。协议层收到后如果 RelayIndicator=1,会自己从合适的口把包再发出去(受控中继)。所以最终包可能还是会从 eth0 出去,但这是协议层主动发的,不是 bridge 泛洪的。
  • 一句话总结:“bridge 闭嘴,转发归协议层说话”——bridge 只管把包递给本机,跨口转发的事由 1905.1 协议层按 relay 规则决定。


    2. EtherType:0x893a

    1905.1 CMDU 在以太网帧里用一个专用的 EtherType 标识:

    EtherType = 0x893a

    这是抓包时识别 1905.1 流量的关键。Wireshark 显示过滤器用 ieee1905,捕获过滤器用 ether proto 0x893a(或 eth.type == 0x893a)。

    如果带 802.1Q VLAN 标签,帧结构是:dst(6) + src(6) + TPID(0x8100) + TCI(2) + EtherType(0x893a) + CMDU。

    代码佐证:prplMesh 定义了带 VLAN 的以太网头结构

    // ieee1905_transport.h
    static constexpr uint16_t ieee_8021q_protocol_id = 0x8100;
    struct ether_header_vlan {


    uint8_t ether_dhost[ETH_ALEN];
    uint8_t ether_shost[ETH_ALEN];
    uint16_t tpid; // 0x8100
    uint16_t tci; // VID + DEI + PCP
    uint16_t ether_type; // 0x893a
    } __attribute__((__packed__));

    2.1 为什么没有 IP 也能通信(纯二层协议)

    关键点:IEEE 1905.1 是纯粹二层以太网协议,完全不使用 IP / UDP / TCP,依靠 MAC 地址完成通信。

    普通网络:IP(三层)→ 查 ARP 得 MAC(二层)→ 网卡发送。 1905.1:跳过 IP 层,直接跑在数据链路层:

  • 目标地址用 MAC
    • 广播/发现:固定组播 MAC 01:80:C2:00:00:13
    • 单播回复:直接填对端硬件/接口 MAC 或 AL MAC(从收到报文的源地址学习),不需要 ARP
  • EtherType = 0x893A
    • 网卡/协议栈看到这个类型,交给 1905 协议栈处理,不送到 IP 协议栈
  • 不需要 ARP
    • ARP 是 IP 用来查 MAC 的;1905 直接用 MAC,没有 ARP 流程
  • 通俗比喻:

    • IP 通信:信封先写【IP 地址】,再查 MAC 交给网卡
    • 1905 通信:信封直接写【MAC 硬件地址】,直接交给网卡发送
    工程限制:不能跨三层 / 跨 VLAN 隔离域

    ⚠️ 重要限制:1905 二层帧不能跨路由器三层转发,也受 VLAN 广播域隔离影响。 设备必须在同一个二层广播域内才能互相发现;跨 VLAN / 三层网关时,1905 组播常被阻断,EasyMesh 搜不到 Agent。 实际工程坑:划分 VLAN 或开了过严的 IGMP/组播抑制后 EasyMesh 发现失败,根源往往是二层组播被隔离或丢弃——不是“没配 IP”。

    Agent 上电找 Controller 的寻址示例(无 IP 参与)

    1. Agent 构造以太网帧
    DST = 01:80:C2:00:00:13(组播发现)
    SRC = Agent 接口 MAC
    EtherType = 0x893A
    MessageType = 0x0007(AP_AUTOCONFIGURATION_SEARCH)

    2. 交换机/桥在同一广播域内传播该组播帧(注意:Linux bridge 对 0x13
    还需 ebtables 禁止泛洪,转发权归协议层,见第 1.3 节)

    3. Controller 收到后,构造单播应答
    DST = 收到报文的源 MAC(Agent)——不需要 IP,不需要 ARP
    SRC = Controller 接口 MAC
    EtherType = 0x893A
    MessageType = 0x0008(AP_AUTOCONFIGURATION_RESPONSE)
    MessageId 与请求相同(用于配对)

    4. Agent 收到单播应答,完成发现

    整个流程没有任何 IP 地址出现,所有寻址完全依靠二层 MAC。

    2.2 Wireshark 过滤模板(入门常用)

    目的
    过滤器
    全部 1905 帧 eth.type == 0x893a 或 ieee1905
    Agent 搜 Controller eth.type == 0x893a && ieee1905.cmdu.type == 0x0007
    Topology Discovery ieee1905.cmdu.type == 0x0000
    EasyMesh 扩展消息(0x8000 起) ieee1905.cmdu.type >= 0x8000
    某 TLV 类型 ieee1905.tlv.type == 0x01(例:AL MAC Address TLV)

    抓包实操提醒:

  • 优先在 LAN 有线侧抓;纯无线 monitor 很难稳定捕获 1905 组播控制面
  • 交换机若开启过严组播抑制,可能丢掉 01:80:C2:00:00:13
  • 看到 1905 帧 ≠ 一定是 EasyMesh:还要看是否有 0x8000+ MAP 消息或 0x80+ MAP TLV(见 EASYMESH-01-EasyMesh入门与概念辨析.md)

  • 3. CMDU 结构(Control Message Data Unit)—— 协议核心

    CMDU 是 1905.1 的基本消息单元。结构分两段:CMDU Header + 若干 TLV。

    3.1 CMDU Header 字段(协议定义)

    字段
    长度
    含义
    messageVersion 1B 消息版本,目前为 0x00
    reservedField0 1B 保留,必须为 0
    messageType 2B 消息类型,决定这条 CMDU 是什么消息(见第 4 节)
    messageId 2B 消息 ID,用于去重和关联请求/响应
    fragmentId 1B 分片号,从 0 开始
    flags 1B 标志位,目前用 2 个 bit:bit7 = LastFragmentIndicator(最后分片标志)bit6 = RelayIndicator(中继标志)bit5~0 保留,必须为 0

    关键点:

    • messageType 决定一切:收到 CMDU 后,先看 messageType 再决定怎么解析后面的 TLV
    • messageId + 源地址 是去重和分片重组的 key
    • RelayIndicator 仅控制组播 CMDU 的中继转发行为,对单播无影响(第 7.4 节详述)

    3.2 代码里的 Header 结构(佐证字段布局)

    prplMesh 用 packed struct 精确还原协议字段:

    // ieee1905_transport.h
    #pragma pack(push, 1)
    struct Ieee1905CmduHeader {


    uint8_t messageVersion;
    uint8_t _reservedField0;
    uint16_t messageType;
    uint16_t messageId;
    uint8_t fragmentId;
    uint8_t flags;
    void SetLastFragmentIndicator(bool value) {


    flags = (flags & ~

    赞(0)
    未经允许不得转载:171主机测评 » EASYMESH-02-ieee1905-1-协议详解
    分享到: 更多 (0)

    评论 抢沙发

    • 昵称 (必填)
    • 邮箱 (必填)
    • 网址