ch12_8
12.8 Mesh Networking (802.11s)
上一节我们还在为 A-MPDU 和 Block Ack 这种「点对点」的高速传输机制喝彩,以为这就是无线网络的终点了。
但现实很快会来打脸——一旦你走出那个覆盖范围只有几十米的房间,或者到了一个没有布线条件的恶劣环境,那个所谓的「高速」AP 就成了摆设。你开始面临一个更原始、更棘手的问题:如何让无线信号像接力赛一样,一跳一跳地传到目的地?
这就是 802.11s 要解决的事。它不再满足于「星型」的中央集权,而是要构建一个真正去中心化的「网状」世界。
从 Ad Hoc 到 Mesh:拓扑的进化
在 802.11s 出现之前,如果你不想用 AP,只能用 IBSS(也就是 Ad Hoc 模式)。 但 Ad Hoc 有个致命弱点:它要求「全互联」。这就好比一群人在黑屋里喊话,每个人必须能直接听到其他所有人的声音,才能保证网络连通。这在现实里几乎不可能——一旦有个节点走到犄角旮旯,或者被墙挡住了,网络就裂了。
Mesh 的出现,就是为了解决这种「脆弱性」。
你可以把 Mesh 想象成一个成熟的社区 gossip(八卦)网络:
- 在 Full Mesh(全互联)里,每个人跟每个人都是朋友,八卦能瞬间传遍全网(这种是理想状态)。
- 在 Partial Mesh(部分互联,更常见)里,你只认识几个邻居,但你相信你的邻居会把话传出去。
在 Linux 内核 2.6.26(2008年)的时候,open80211s 项目把 802.11s 的草案支持塞进了无线协议栈。这得感谢 OLPC(一个百元笔记本计划)和一些厂商的赞助,还有 Luis Carlos Cobo 和 Javier Cardona 等人在 Cozybit 写下的早期代码。他们让 Linux 成为第一个在内核层面原生支持 Mesh 的通用操作系统。
HWMP 协议:Layer 2 的路由心
既然是「网状」,数据包从 A 到 C 可能要经过 B,那么 B 怎么知道要把包扔给谁? 通常我们想用 IP 路由(Layer 3),但 802.11s 是个二层协议——它根本不想知道 IP 是什么。 于是,它发明了自己的路由协议:HWMP (Hybrid Wireless Mesh Protocol)。
为什么叫「混合」? 因为它结合了两种截然不同的路由策略,像是在「懒人」和「忙人」之间反复横跳:
-
On-Demand Routing(按需路由)——「急用现找」
- 平时不维护路由表,只有当你要给某人发数据,却发现手里没路(
mesh_path_lookup失败)时,才发起 PREQ (Path Request)。 - 这就像你要找人借钱,突然想起来没他号码,于是站在村口喊:「我要找张三!谁知道张三在哪?」
- PREQ 会在 Mesh 里广播(
IEEE80211_STYPE_ACTION),经过的每个节点都会记下「是谁在找张三」,直到传到张三耳朵里。
- 平时不维护路由表,只有当你要给某人发数据,却发现手里没路(
-
Proactive Routing(主动路由)——「树状广播」
- 有些节点是「大忙人」(流量汇聚点),大家都在找它。这时候让所有人都去按需查找太慢了。
- 这个节点会自封为 Root Mesh Point,周期性发 RANN (Root Announcement):我是根,来连接我吧。
- 其他节点收到后,就会主动建立一条指向根的「树形」路径。
寻路与缓存:PREQ/PREP 的二重奏
让我们把镜头拉近,看看一个数据包是如何在 Mesh 里找到路的。
第一步:迷路与喊话(PREQ)
假设节点 A 要给节点 D 发数据。
A 先查自己的 mesh_path 表(在 net/mac80211/mesh.h 里定义)。
- 没路? 触发
hwmp_preq_frame_process(),发一个 PREQ 广播包。 - 此时,A 不能干等着,数据还得发。A 会把这些还没发出的数据包塞进一个叫
frame_queue的缓冲区(每个mesh_path对象都有一个)。 - 注意,这个缓冲区很小,最多只能存 10 个包(
MESH_FRAME_QUEUE_LEN)。塞满了就丢,爱莫能助。
第二步:响应与回传(PREP)
PREQ 一路跳,最终到达 D。
D 收到后,发现:「哎,这找的是我啊。」
它会顺着 PREQ 进来的反向路径,单播回一个 PREP (Path Reply)。
PREP 里带着路径信息,一路回到 A。
A 收到 PREP,终于建立了到 D 的完整路由(存储在 mesh_path 结构里)。
第三步:释放积压
还记得刚才塞进 frame_queue 的那几个包吗?
路由一建立,mesh_path_tx_pending() 就会被调用,把那 10 个积压包顺着重获新生的路由一股脑发出去。
那一刻,就像疏通的管道重新流淌。
第四步:如果路断了(PERR) 无线环境是动荡的。中间某个节点 B 可能没电了,或者被干扰了。 B 会发出一个 PERR (Path Error),通知大家:「别往我这发了,前面路断了。」 A 收到 PERR 后,可能得重新来过,再发一次 PREQ。
路由的代价:Airtime Metric
你可能会问:如果有两条路通向 D,HWMP 选哪条? 这不是看跳数最少,而是看 Airtime Metric(空时度量)。
这是一个非常务实的指标。它计算的是:在这个链路上传一个数据包,大概要占用多少微秒的信道时间。
计算逻辑在 airtime_link_metric_get() 里,大概长这样:
开销 = (数据包长度 + 固定开销) / 传输速率
- 如果你的信号好(速率高),开销就小,Metric 就低(越好)。
- 如果你的信号烂(速率低,误包率高),开销就大,Metric 就高(越烂)。
所以,HWMP 会倾向于选择那条「虽然多跳一跳,但每跳都很快」的路,而不是「虽然只有两跳,但每步都像蜗牛」的路。
实战:用 iw 搭建 Mesh
说了这么多,怎么在 Linux 上把网卡变成 Mesh 节点?
首先,你手里得有一块支持 Mesh 模式的硬件(很多旧网卡或只有固件支持不行的驱动会在这里卡住)。
然后,忘掉老古董 iwconfig,请出新时代的标准工具 iw。
1. 创建 Mesh 接口
# 把 wlan0 设置为 Mesh Point 模式
iw wlan0 set type mesh
# 或者简写为 mp
iw wlan0 set type mp
2. 加入 Mesh 网络 就像给 Wi-Fi 设置 SSID 一样,Mesh 网络也有一个 ID。
iw wlan0 mesh join "my-mesh-ID"
只要两个以上的节点执行相同的 mesh join 命令(并且在射频范围内),它们就会开始握手,交换 PREQ/PREP,建立路由。
3. 验证状态 这时候你像上帝一样俯瞰网络:
# 查看有哪些邻居(1-hop邻居)
iw wlan0 station dump
# 查看路由表(整个 Mesh 的路径)
iw wlan0 mpath dump
在 mpath dump 的输出里,你能看到 NEXT_HOP(下一跳)和 METRIC(开销)。这正是 HWMP 算法劳动的成果。
是药三分毒:Mesh 的代价
Mesh 听起来很美(覆盖广、自愈强、无需布线),但别急着扔掉你的网线,它有明显的副作用:
- 广播风暴:HWMP 的 PREQ 是广播的,路由维护也是广播的。在大型 Mesh 网络里,管理帧能占掉一大半带宽,你的用户数据只能在夹缝里求生存。
- 性能瓶颈:每一跳都会带来延迟和吞吐量的衰减。从 A 到 B 经过 5 跳,你的千兆网卡可能只能跑出百兆的速度,还要忍受极高的 Ping 值。
- 驱动地狱:虽然内核支持了,但很多厂商的无线驱动压根没实现 Mesh 的回调函数,或者实现得有 Bug。你可能会发现网卡能连上 AP,但一进 Mesh 模式就内核崩溃。
小结与预告
这一节,我们走出了单点 AP 的舒适区,进入了一个更复杂、更动态的分布式世界。
从 IBSS 的「全互联幼稚园」,进化到了 Mesh 的「多跳成熟社会」。我们看到了 HWMP 如何用 PREQ/PREP 在 Layer 2 上玩转路由,也看到了 Linux 如何通过 mac80211 和 iw 将这些复杂的协议封装成几条简单的命令。
但是,无论是有线还是无线,Linux 的网络栈终归是要跑在硬件上的。 下一章,我们将把视线从空中的无线电波,切换到更高性能、更昂贵的硬件世界——InfiniBand 和 RDMA。 在那里,延迟将被以微秒计算,内核的角色也将发生巨大的转变。