> /technology/

用 Raspberry Pi 5 与 Zero 2 W 搭建 OONF/OLSRv2 IBSS 自组网

记录使用 Raspberry Pi 板载 Wi-Fi、OONF/OLSRv2 和 IBSS 构建多跳自组网的配置、仿真与排障过程。

  • Raspberry Pi
  • OONF
  • OLSRv2
  • IBSS
  • MANET
  • Linux Networking

本文记录一套已运行验证的无线自组网方案:使用 Raspberry Pi 5 和 Raspberry Pi Zero 2 W 的板载 Wi-Fi 组成 IBSS(ad-hoc)网络,由 OONF 中的 OLSRd2 发现邻居、计算链路代价,并向 Linux 内核安装多跳路由。

实验目标是构造四节点三跳网络,并在链路质量变化后观察路由切换:

10.10.0.1 <-> 10.10.0.2 <-> 10.10.0.3 <-> 10.10.0.4
   node1          node2          node3          node4

node1 到 node4 的预期数据路径:
node1 -> node2 -> node3 -> node4

本文重点记录四类问题:/24 connected route 对 OLSR 路由的影响;业务丢包增加但 OLSRd2 仍选择直连;tc netem 规则误伤相邻节点;指定 IBSS BSSID 后 Broadcom 驱动仍加入随机 Cell ID。

本文对应的实测软件版本为 OONF/OLSRd2 0.15.1,Git 提交 b2164126;运行节点之一为 Raspberry Pi 5,64 位 Raspberry Pi OS,内核 6.18.34+rpt-rpi-2712。不同内核、固件和无线芯片上的 IBSS 行为可能不同。

1. 硬件背景与选择

Raspberry Pi 5

Raspberry Pi 5 具有更强的 CPU、更多内存、千兆以太网和双频 802.11ac Wi-Fi,主要用于编译 OONF、记录日志、运行监控页面和执行 iperf3 压测。硬件信息见 Raspberry Pi 5 产品说明

在这套网络中,Pi 5 用作开发、边缘计算或地面监控节点。它的功耗和体积高于 Zero 2 W,高负载运行时需要可靠供电和散热。

Raspberry Pi Zero 2 W

Zero 2 W 使用四核 64 位 Cortex-A53、512 MB 内存和 2.4 GHz 802.11 b/g/n Wi-Fi,适合作为轻量移动节点。详细规格见 Raspberry Pi Zero 2 W 官方页面

它只有 2.4 GHz Wi-Fi,因此混合组网时所有设备都应使用双方都支持的 2.4 GHz 信道。本文选择 2452 MHz,即 2.4 GHz 信道 9。

两类设备共同需要注意的地方

  • 两者的板载 Wi-Fi 都使用 Broadcom/Cypress brcmfmac 驱动栈。IBSS 支持取决于具体的内核和固件组合。
  • Zero 2 W 虽然 CPU 支持 64 位,但系统可能安装为 32 位。不能把 Pi 5 上编译的 AArch64 二进制直接复制到 32 位系统。
  • 板载天线位置、设备安装姿态、供电噪声和外壳会影响实际链路质量。
  • 实验时应关闭 Wi-Fi 省电,避免将休眠导致的时延和丢包误判为路由协议问题。
  • IBSS 本身不提供安全保障。本文配置仅适用于受控实验环境;生产网络还需要配置认证、加密、防火墙和管理面隔离。

2. 整体架构

系统分为四层,各层关系如下:

应用流量(ping / iperf3 / 业务程序)
                |
Linux IPv4 路由与转发
                |
OONF:NHDP + OLSRv2 + FF DAT metric
                |
Linux nl80211/cfg80211 + brcmfmac
                |
wlan0:2.4 GHz IBSS

各层职责如下:

  1. iwwlan0 切换到 IBSS 模式,并加入相同 SSID、频率和 Cell。
  2. 每个节点在 wlan0 上使用唯一的 /32 IPv4 地址。
  3. NHDP 通过 HELLO 消息发现一跳和二跳邻居,并判断链路是否对称。
  4. OLSRv2 传播拓扑信息,使用链路 metric 运行最短路径计算。
  5. OLSRd2 把计算结果安装到 Linux 主路由表。
  6. 中间节点依靠 Linux ip_forward 转发实际业务数据。

OLSRv2 是主动式链路状态协议,持续维护网络拓扑并预先计算路由。协议定义见 RFC 7181,邻居发现由 RFC 6130(NHDP) 定义。

3. 系统准备

所有节点建议使用相近的 Raspberry Pi OS、内核和固件版本。先记录运行基线:

uname -a
cat /proc/device-tree/model
iw dev
iw phy phy0 info
rfkill list

安装编译和诊断工具:

sudo apt update
sudo apt install -y \
  git cmake build-essential pkg-config \
  libnl-3-dev libnl-genl-3-dev \
  iw iproute2 nftables iperf3 curl

解除无线软阻塞:

sudo rfkill unblock wifi

避免 NetworkManager 抢占 wlan0

IBSS 脚本需要直接控制 wlan0。如果 NetworkManager 或 wpa_supplicant 同时管理该接口,接口可能在服务启动时被切回 managed 模式,或在加入 IBSS 后立即断开。

创建 /etc/NetworkManager/conf.d/99-unmanaged-wlan0.conf

[keyfile]
unmanaged-devices=interface-name:wlan0

然后重启 NetworkManager:

sudo systemctl restart NetworkManager
nmcli dev status

预期 wlan0 显示为“未托管”或 unmanaged。不要因此禁用整台机器的 NetworkManager;Pi 5 可能仍需要通过以太网或另一块无线网卡连接管理网络。

4. 编译 OONF/OLSRd2

克隆 OONF 官方仓库并编译:

cd /home/pi/code
git clone https://github.com/OLSR/OONF.git
cd OONF

cmake -S . -B build -DCMAKE_BUILD_TYPE=Release
cmake --build build -j"$(nproc)"

安装静态版 OLSRd2:

sudo install -m 0755 build/olsrd2_static /usr/local/bin/olsrd2_static
/usr/local/bin/olsrd2_static --version

静态版把 nhdpolsrv2ff_dat_metricneighbor_probinghttpnhdpinfoolsrv2infonetjsoninfo 等插件编入一个二进制。部署到资源较小的节点时,可以避免逐个处理动态库。

Zero 2 W 的本地编译速度较慢。可以在相同架构和 ABI 的 Pi 5 上构建后复制二进制,但必须先检查:

uname -m
file /usr/local/bin/olsrd2_static
ldd /usr/local/bin/olsrd2_static

aarch64armv7l 不能混用。olsrd2_static 只是 OONF 插件的静态组合,不代表完全不依赖系统动态库。

5. 用配置文件描述节点

将节点参数放在 /etc/fanet-ibss.env

IFACE=wlan0
IBSS_SSID=fanet-ibss
IBSS_FREQ=2452
IBSS_BSSID=
IBSS_ADDR=10.10.0.1/32
IBSS_JOIN_RETRIES=5
IBSS_JOIN_RETRY_SEC=2
IBSS_LINK_WAIT_SEC=10
IBSS_STRICT_BSSID=false
IBSS_STATE_FILE=/run/fanet-ibss.bssid
OLSRD2_BIN=/usr/local/bin/olsrd2_static
OLSRD2_CONFIG=/etc/olsrd2/fanet.conf

四台设备使用相同配置,仅修改节点地址:

节点 地址
node1 10.10.0.1/32
node2 10.10.0.2/32
node3 10.10.0.3/32
node4 10.10.0.4/32

这里必须使用 /32。如果使用 10.10.0.x/24,Linux 会自动生成 10.10.0.0/24 dev wlan0 connected route,并将所有节点视为二层直连目标。即使 OLSRd2 计算出经中间节点的路径,内核已有的 on-link 判断也可能掩盖或干扰实验结果。

6. IBSS 加入脚本

加入 IBSS 的基本顺序如下:

sudo nmcli dev disconnect wlan0 || true
sudo nmcli dev set wlan0 managed no || true

sudo ip link set wlan0 down
sudo iw dev wlan0 set type ibss
sudo ip link set wlan0 up
sudo iw dev wlan0 set power_save off
sudo iw dev wlan0 ibss leave || true

sudo iw dev wlan0 ibss join fanet-ibss 2452
sudo ip addr flush dev wlan0
sudo ip addr add 10.10.0.1/32 dev wlan0

需要固定 Cell ID 时,使用以下命令形式:

sudo iw dev wlan0 ibss join fanet-ibss 2452 fixed-freq 76:94:53:5C:9A:E5

不要随意调整参数顺序,也不要替换成未经验证的等价写法。对 brcmfmac 而言,用户空间命令执行成功不代表固件一定使用指定的 BSSID。

脚本还需要处理以下情况:

  • nmcli dev disconnect 在接口本来就未激活时会返回错误,所以必须允许失败。
  • brcmfmac 可能先返回 Operation already in progress (-114),随后异步完成加入。应再次读取 iw dev wlan0 link,不能立即判定失败。
  • 加入后要同时验证 iw dev wlan0 info 的类型和 iw dev wlan0 link 的实际 BSSID,并把结果写到 /run/fanet-ibss.bssid

检查结果:

iw dev wlan0 info
iw dev wlan0 link
ip -4 addr show dev wlan0
cat /run/fanet-ibss.bssid

四台设备必须看到同一个 Cell BSSID。SSID 和频率相同但 Cell ID 不同,仍然是两个互不连通的 IBSS 网络。

7. 配置 OLSRd2

创建 /etc/olsrd2/fanet.conf

[interface=wlan0]
    ffdat_unicast true
    ffdat_loss_exponent cubic

[neighbor_probing]
    interval 0.2
    size 512

[http]
    bindto 0.0.0.0
    port 1980
    webserver /usr/local/share/fanet-olsrd2-www

[log]
    debug nhdp
    debug ff_dat_metric
    debug olsrv2_routing

主要参数如下:

  • ffdat_unicast true:允许单播样本参与链路 metric 计算。
  • ffdat_loss_exponent cubic:提高丢包对链路代价的影响,丢包链路会更快变贵。
  • neighbor_probing.interval 0.2:以较高频率探测缺少单播业务的邻居。
  • neighbor_probing.size 512:探测消息正文大小为 512 字节,外部还会有 RFC5444、UDP、IP 和链路层开销。
  • HTTP 1980:提供状态查询和本地监控页面。
  • debug 日志:实验阶段保留;稳定部署后应降低日志级别,减少写盘和无关日志。

先做配置语法检查:

sudo /usr/local/bin/olsrd2_static \
  --load /etc/olsrd2/fanet.conf \
  --set=global.lockfile=- \
  --quit

如果已有 OLSRd2 正在监听 HTTP 或 telnet 端口,验证时可能出现端口占用警告。应结合退出码判断配置是否可解析,不能只根据警告文本判断失败。

8. 用 systemd 开机启动

创建 /etc/systemd/system/fanet-ibss.service,让 systemd 负责配置 IBSS 并启动 OLSRd2:

[Unit]
Description=Configure wlan0 IBSS and run OLSRd2
Wants=NetworkManager.service wpa_supplicant.service sys-subsystem-net-devices-wlan0.device
After=NetworkManager.service wpa_supplicant.service sys-subsystem-net-devices-wlan0.device
Before=network.target

[Service]
Type=simple
EnvironmentFile=/etc/fanet-ibss.env
ExecStartPre=/usr/local/sbin/fanet-ibss-setup.sh
ExecStartPre=/bin/sh -c 'exec "${OLSRD2_BIN:-/usr/local/bin/olsrd2_static}" --load "${OLSRD2_CONFIG:-/etc/olsrd2/fanet.conf}" --quit'
ExecStart=/bin/sh -c 'exec "${OLSRD2_BIN:-/usr/local/bin/olsrd2_static}" --load "${OLSRD2_CONFIG:-/etc/olsrd2/fanet.conf}"'
Restart=always
RestartSec=5

[Install]
WantedBy=multi-user.target

启用服务:

sudo systemctl daemon-reload
sudo systemctl enable --now fanet-ibss.service
systemctl status fanet-ibss.service --no-pager
journalctl -u fanet-ibss.service -n 120 --no-pager

修改环境文件或 OLSRd2 配置后,重启服务加载变更:

sudo systemctl restart fanet-ibss.service

OONF remotecontrol/telnet 动态加载在当前版本上不够可靠:config load file://...nc 会话和配置提交行为存在不一致,部分操作还会重建运行状态并触发 systemd 重启。因此配置文件作为唯一事实来源,修改后通过重启服务加载。路由守护进程重启通常只持续几秒,配置行为更容易预测和排查。

确认内核转发

OLSRd2 负责学习并安装路由,IP 数据包由 Linux 内核转发。检查:

sysctl net.ipv4.ip_forward

应为:

net.ipv4.ip_forward = 1

OONF 的 Linux 接口层会尝试为 mesh 接口开启转发,失败时输出警告。为避免依赖运行时行为,可以持久化配置:

# /etc/sysctl.d/90-fanet-forwarding.conf
net.ipv4.ip_forward=1
sudo sysctl --system

OLSR 控制消息的协议转发与 Linux 业务 IP 包转发是两套机制:前者属于协议泛洪,后者由 ip_forward 控制。

9. OLSRv2 路径选择

邻居发现、拓扑传播和内核路由

NHDP 通过 HELLO 消息建立一跳、二跳邻居关系并判断链路对称性。OLSRv2 通过 TC 消息传播拓扑信息,并使用 MPR 减少泛洪。OLSRd2 对已知拓扑运行 Dijkstra 计算,选择累计 pathcost 最小的路径,再将下一跳写入内核路由表。

因此:

  • 能 ping 通不代表 NHDP 认为链路质量良好。
  • NHDP 仍显示邻居不代表 OLSRv2 必须选择该直连链路。
  • ip route 中的 Linux route metric 通常不是 OLSR 内部的完整 pathcost。内部代价应从 nhdpinfoolsrv2info 或 debug 日志观察。

FF DAT metric

当前配置使用 ff_dat_metric,它综合报文成功率和二层吞吐信息。源码中的 ffdat_loss_exponent 支持 linearquadraticcubicdynamic

将报文成功率记为 p,只考虑丢包惩罚时,OONF 0.15.1 源码的实际计算近似为:

linear:    cost factor ≈ 1 / p
quadratic: cost factor ≈ 1 / p²
cubic:     cost factor ≈ 1 / p⁴

需要注意,cubic 分支先将内部 exponent 设为 3,随后循环两次执行“当前成功率乘以自身”,因此 p 依次变为 p⁴,不是字面意义上的 。这是 OONF 0.15.1 的实现行为,其他版本应以对应源码和实测数据为准。

例如成功率为 70%:

linear    ≈ 1.43
quadratic ≈ 2.04
cubic     ≈ 4.16

这不是完整的 DAT pathcost 公式。实际实现还会处理吞吐率、可用的 layer-2 数据、采样窗口和迟滞;该近似可以说明 cubic 为什么会明显放大有损链路的代价。

为什么需要 neighbor probing

如果链路上长期没有单播业务,驱动可能没有足够的新数据估计单播性能。neighbor_probing 会轮询已知邻居;当某条链路近期没有单播流量时,发送 RFC5444 探测消息。本文使用 0.2 秒间隔和 512 字节正文,以便更快观察 metric 变化。

10. 使用 ip route get 验证路径

假设本机是 10.10.0.1

直连邻居可能显示:

10.10.0.2 via 10.10.0.2 dev wlan0 src 10.10.0.1

目标地址同时作为 gateway 看起来不直观,但这可能是 OLSRd2 为一跳邻居安装的 /32 主机路由,不代表路由未生效。

经 node2 到 node3 的多跳结果应类似:

10.10.0.3 via 10.10.0.2 dev wlan0 src 10.10.0.1

检查时同时使用:

ip route get 10.10.0.3
ip route show table main
ip neigh show dev wlan0

不能只根据一次 ip route get 判断 OLSR 是否完成转发,还应在中间节点抓包或观察计数:

sudo tcpdump -ni wlan0 host 10.10.0.1 and host 10.10.0.3

11. 构造四节点三跳场景

真实无线环境难以稳定制造“node1 只能经 node2 到 node3”的链路条件,因此使用 tc netemnftables 构造可重复的实验场景。

期望保留的好链路是:

node1 <-> node2
node2 <-> node3
node3 <-> node4

应恶化的非相邻直连关系是:

本地节点 恶化目标
node1 node3、node4
node2 node4
node3 node1
node4 node1、node2

数据面:tc netem

wlan0 根部安装 prio qdisc:默认流量进入干净队列 1:2,只有目的 MAC 匹配非相邻节点的帧进入带 netem1:3 及后续队列。

可控参数包括:

  • BAD_LOSS:数据面随机丢包。
  • BAD_DELAY:固定附加时延。
  • BAD_JITTER:时延抖动。
  • BAD_RATE:可选速率上限。

推荐从温和参数开始:

BAD_LOSS=15%
BAD_DELAY=15ms
BAD_JITTER=10ms

如果直连仍明显更便宜,再逐级增加参数,例如 35%/30ms/15ms,不要一开始使用 90% 丢包。控制面丢包过高会使邻居过期,测试结果将变成拓扑断开,而不是 metric 切换。

控制面:nftables UDP/269

控制面是该模拟方案的另一部分。

如果只对业务单播应用 tc,ping 和 iperf3 可能已经变差,但 OLSRd2 的 HELLO、TC 和 probe 仍能正常到达。此时 ff_dat_metric 观测到的直连质量仍然较好,路由不会切换。

OONF 的 RFC5444 MANET 默认使用 UDP 269 端口,neighbor_probing 也将 probe 封装为 RFC5444 消息。因此需要按比例丢弃来自非相邻节点的入站 UDP/269,让 metric 采样到链路恶化:

sudo nft add table inet fanet_olsr_impair
sudo nft add chain inet fanet_olsr_impair input \
  '{ type filter hook input priority -20; policy accept; }'

sudo nft add rule inet fanet_olsr_impair input \
  iifname wlan0 ip saddr 10.10.0.3 udp dport 269 \
  numgen random mod 100 '<' 60 counter drop

清理:

sudo nft delete table inet fanet_olsr_impair
sudo tc qdisc del dev wlan0 root

每个方向都必须单独配置。无线链路 metric 是有方向的,只在 node1 丢弃发往 node3 的包,不会自动使 node3 认为 node1 的链路质量较差。

应用与验证顺序

  1. 四台设备先清理全部旧 tc/nft 规则。
  2. 验证无模拟时所有相邻和非相邻节点的基线 ping、吞吐和 OLSR metric。
  3. 每台设备使用正确的 ROLE、节点 IP 和真实 wlan0 MAC 应用规则。
  4. 检查 qdisc/filter/nft counter,确认规则确实命中。
  5. 等待多个探测周期和拓扑收敛时间,再观察 route。
  6. 分别测试 node1 -> node4 和 node4 -> node1,不假设双向结果相同。

常用命令:

tc -s qdisc show dev wlan0
tc filter show dev wlan0 parent 1:
sudo nft list table inet fanet_olsr_impair

ip route get 10.10.0.4
ping -c 20 10.10.0.4
iperf3 -c 10.10.0.4 -t 20
iperf3 -c 10.10.0.4 -u -b 5M -t 20

在目标节点先启动服务端:

iperf3 -s

12. 监控与排障

第一层:无线 Cell

iw dev wlan0 info
iw dev wlan0 link
cat /run/fanet-ibss.bssid

确认接口类型、SSID、频率和 BSSID。若四台设备的 BSSID 不一致,应先修复 IBSS,再检查 OLSR。

第二层:地址、邻居和转发

ip -4 addr show dev wlan0
ip neigh show dev wlan0
sysctl net.ipv4.ip_forward

确认地址唯一且使用 /32,中间节点的 forwarding 为 1。

第三层:OLSRd2 服务与日志

systemctl status fanet-ibss.service --no-pager
journalctl -u fanet-ibss.service -n 200 --no-pager
journalctl -k -n 200 --no-pager

服务日志用于检查 NHDP、DAT metric 和路由选择;内核日志用于检查 brcmfmaccfg80211 和固件异常。两类日志需要分别检查。

第四层:OONF 状态接口

当前监控页面使用以下 HTTP endpoint:

/telnet/netjsoninfo%20graph
/telnet/nhdpinfo%20json%20neighbor
/telnet/nhdpinfo%20json%20link
/telnet/olsrv2info%20json%20route
/telnet/systeminfo%20json%20version

直接验证:

curl -fsS --max-time 3 \
  'http://127.0.0.1:1980/telnet/nhdpinfo%20json%20link'

curl -fsS --max-time 3 \
  'http://127.0.0.1:1980/telnet/olsrv2info%20json%20route'

监控前端不应将所有请求放进不可分割的 Promise.all。链路变化时某个 endpoint 暂时失败,不应清空其他已成功的面板。应逐个请求或使用 Promise.allSettled,保留旧数据,并将页面状态标记为 PARTIAL

13. 问题、原因与处理

问题 1:使用 /24,导致所有节点表现为直连

现象: ip route get 对所有 10.10.0.x 都表现为 on-link,预期的中间下一跳不明显。

原因: Linux 自动生成覆盖整个 mesh 网段的 connected route,并优先在二层解析最终目标。

处理: 每个 mesh 地址使用 /32,由 OLSRd2 为每个目标安装明确的主机路由。

问题 2:将 via 目标地址 误判为路由未生效

现象: 一跳邻居显示 10.10.0.3 via 10.10.0.3 dev wlan0

原因: 这可能是一条以邻居本身为下一跳的 /32 路由。

处理: 对多跳目标检查 gateway 是否变为中间节点,并结合 OLSRv2 route endpoint、邻居表和抓包判断。

问题 3:ping 已恶化,但 OLSRd2 仍选择直连

现象: 非相邻节点的 ping 已出现丢包和高时延,但 OLSR route 没有变化。

原因: tc 只恶化了业务数据;OLSR 控制包和 probe 未受到同样影响,ff_dat_metric 仍认为直连链路代价较低。

处理: 同时处理数据面和控制面。对入站 UDP/269 按源 IP 配置随机 drop,并观察 nft counter 是否增长。

问题 4:只构造一个方向的丢包

现象: node1 的路由发生变化,但 node3 的反向路由没有变化,或链路表现出不对称状态。

原因: OLSRv2 分别维护入向和出向 metric;无线损耗和模拟规则同样具有方向性。

处理: 明确配置每个节点的恶化目标,不假设一条 nft/tc 规则会自动作用于反方向。

问题 5:tc 默认 class 误伤相邻节点

现象: 只计划恶化 node1 -> node3,但 node1 -> node2 的 ping 和吞吐也变差。

原因: prio 的默认队列挂载了 netem,未命中 filter 的普通流量也进入了坏队列。

处理: 保留 1:2 作为干净默认队列,非相邻目标从 1:3 开始;应用后检查 tc -s 的逐队列计数。

问题 6:规则已配置,但计数几乎不增长

现象: 已提高丢包和时延参数,但路由仍不变化,tc 或 nft counter 增长很少。

原因: 规则可能匹配了错误的 MAC、IP、方向或端口;控制包也可能绕过只按目的 MAC 配置的数据面规则。

处理:ip link show wlan0 重新确认 MAC;使用 tcpdump -ni wlan0 udp port 269 验证实际方向;分别检查 tc filter 和 nft counter。

问题 7:恶化参数过大,结果变成断网而不是选路

现象: 邻居直接消失,拓扑反复震荡,路由在存在和消失之间变化。

原因: 90% 以上的控制面丢包可能使 NHDP 无法保持对称邻居,OLSR 没有可比较的直连候选。

处理: 从 15% 到 35% 逐级增加,利用 cubic loss exponent 放大差异;只有直连仍持续占优时才继续提高。

问题 8:固定 BSSID 命令成功,但实际加入随机 Cell

现象: 使用已验证的命令指定 76:94:53:5C:9A:E5,但 iw link 最终显示另一个随机 BSSID,节点分裂到不同 Cell。

原因: 一次内核和 raspi-firmware 更新后,板载 Broadcom 固件/驱动开始忽略固定 IBSS BSSID;日志还出现过 cfg80211_roamed warning。问题不只是 shell 参数错误。

处理:

  • 保留已验证的 fixed-freq BSSID 命令形式,不直接修改语法。
  • 始终读取实际 BSSID,不能根据 iw 返回码判断成功。
  • IBSS_STRICT_BSSID=false 时记录警告并继续;严格实验时设为 true,校验失败立即退出。
  • 如果固定 Cell ID 是硬性要求,使用已验证的旧内核/固件组合,或改用 IBSS 支持更稳定的 ath9k/mt76 外置网卡。
  • 临时方案可以不指定 BSSID,由第一台设备创建 Cell,其余设备加入相同 SSID/频率的网络;每次仍必须核对最终 BSSID。

问题 9:将 brcmfmac 的异步返回判定为失败

现象: iw 返回 Operation already in progress (-114)Link has been severed (-67),systemd 立即退出并反复重启。

原因: 固件事件是异步的,命令报错后加入过程可能仍在继续。

处理: 加入命令失败后短暂轮询 iw dev wlan0 link;超过重试和等待窗口仍未出现 Joined IBSS 时才判定失败。

问题 10:NetworkManager 的非致命错误触发服务失败

现象: 日志出现 This device is not active 和“未断开所有设备”,随后 setup 退出。

原因: 对本来就未连接的设备执行 disconnect 会返回非零;在启用 set -e 的脚本中会成为致命错误。

处理: 使用 nmcli dev disconnect "$IFACE" || true,随后明确设置 managed no。同时保留永久的 unmanaged 配置,减少启动竞争。

问题 11:systemd 反复重启掩盖原始错误

现象: restart counter 快速增加,日志中只能看到 OLSRd2 未启动。

原因: 实际失败通常发生在 ExecStartPre 的 IBSS 校验;Restart=always 只是不断重复同一错误。

处理: 先查看该次启动前的 setup 输出和内核日志:

journalctl -u fanet-ibss.service -b --no-pager
journalctl -k -b --no-pager

修复 Cell、接口类型或配置语法后,再重启服务。

问题 12:尝试热加载所有 OLSRd2 参数

现象: telnet/remotecontrol 命令超时,文件 loader 不识别 URL,提交配置后守护进程状态大幅重建。

原因: 配置系统支持的运行期修改范围有限,不能将任意完整配置无损热替换;shell/netcat 会话还增加了额外的不确定性。

处理: 使用 /etc/olsrd2/fanet.conf 启动,通过 systemctl restart fanet-ibss.service 加载变更。热更新需要在逐项确认插件语义后再评估。

问题 13:配置检查与运行实例发生端口冲突

现象: 使用 --quit 验证时报告 HTTP 1980 或 telnet 2009 已占用。

原因: 当前 OLSRd2 仍在运行,验证进程也会初始化相关插件。

处理: 使用独立 lockfile 参数,结合退出码和具体警告判断。若需要完全无端口占用警告的验证,先停止服务,但这会暂时中断路由。

问题 14:未开启 Linux IP forwarding

现象: 各节点都有正确路由,中间节点也能访问两端,但端到端业务流量仍在中间节点停止。

原因: 路由守护进程和内核转发开关是两套独立机制。

处理: 在所有可能承担中继的节点确认 net.ipv4.ip_forward=1,并检查防火墙的 forward chain。

问题 15:监控页一个请求失败,所有数据一起消失

现象: 路由收敛期间,页面统一显示 TypeError: Failed to fetch

原因: 多个 endpoint 被放进单个 Promise.all,任意一个请求失败都会使整个批次 rejected。

处理: 独立刷新各面板或使用 Promise.allSettled;失败面板标记 ERR,全局标记 PARTIAL,保留最近一次成功数据和时间戳。

问题 16:过早将运行时放入 Docker

现象: 容器需要 host network、NET_ADMIN、设备映射和接近 privileged 的权限,systemd、NetworkManager、iwtcnft 的控制边界变得复杂。

原因: 该程序需要直接操作宿主机无线设备、路由表和内核网络栈,容器无法提供明显的有效隔离。

处理: 运行时使用宿主机 systemd。Docker 可用于可重复编译或 CI,但不作为这套板载 Wi-Fi 控制方案的默认运行环境。

14. 一套可靠的验收清单

按以下顺序检查,逐层确认配置和运行状态:

# 1. 硬件和驱动
cat /proc/device-tree/model
iw dev wlan0 info
iw dev wlan0 link

# 2. 地址和 Cell
ip -4 addr show dev wlan0
cat /run/fanet-ibss.bssid

# 3. 内核转发
sysctl net.ipv4.ip_forward

# 4. 守护进程
systemctl is-active fanet-ibss.service
journalctl -u fanet-ibss.service -n 120 --no-pager

# 5. OLSR 邻居、链路和路由
curl -fsS 'http://127.0.0.1:1980/telnet/nhdpinfo%20json%20neighbor'
curl -fsS 'http://127.0.0.1:1980/telnet/nhdpinfo%20json%20link'
curl -fsS 'http://127.0.0.1:1980/telnet/olsrv2info%20json%20route'

# 6. 内核选路
ip route show table main
ip route get 10.10.0.4

# 7. 模拟规则及命中量
tc -s qdisc show dev wlan0
tc filter show dev wlan0 parent 1:
sudo nft list table inet fanet_olsr_impair

# 8. 业务数据
ping -c 20 10.10.0.4
iperf3 -c 10.10.0.4 -t 20

上一层确认正常后再分析下一层。例如 BSSID 已分裂时,调整 OLSR metric 没有意义;UDP/269 drop counter 为零时,继续增加丢包比例也没有意义。

15. 结论

Raspberry Pi 板载 Wi-Fi 可以运行 OONF/OLSRv2 多跳实验。实验结果取决于以下配置是否同时正确:Linux connected route、IBSS Cell 一致性、Broadcom 固件行为、OONF 控制面采样、DAT metric、内核 forwarding,以及流量模拟规则。

本次实验得到以下结论:

  • mesh 节点地址使用 /32,将每个目标的下一跳交给 OLSRd2。
  • 配置保存为文件,通过 systemd 重启加载,不依赖全量热更新。
  • 读取实际 IBSS BSSID,不能仅根据命令返回成功判断加入结果。
  • 测试 metric 切换时,同时处理数据面和 UDP/269 控制面。
  • tc 默认队列保持干净,只让明确匹配的非相邻链路进入 netem
  • 使用 OONF 内部 metric、内核路由和业务流量测试交叉验证。
  • 运行时由宿主机 systemd 管理,不为无线设备控制引入额外的容器权限层。

OLSRv2 根据采集到的有向链路 metric 计算累计 pathcost,并选择代价最小的可用路径。业务 ping 的结果只有在影响到控制面采样后,才可能触发路由切换。

参考资料