> /technology/
用 Raspberry Pi 5 与 Zero 2 W 搭建 OONF/OLSRv2 IBSS 自组网
记录使用 Raspberry Pi 板载 Wi-Fi、OONF/OLSRv2 和 IBSS 构建多跳自组网的配置、仿真与排障过程。
本文记录一套已运行验证的无线自组网方案:使用 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
各层职责如下:
iw把wlan0切换到 IBSS 模式,并加入相同 SSID、频率和 Cell。- 每个节点在
wlan0上使用唯一的/32IPv4 地址。 - NHDP 通过 HELLO 消息发现一跳和二跳邻居,并判断链路是否对称。
- OLSRv2 传播拓扑信息,使用链路 metric 运行最短路径计算。
- OLSRd2 把计算结果安装到 Linux 主路由表。
- 中间节点依靠 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
静态版把 nhdp、olsrv2、ff_dat_metric、neighbor_probing、http、nhdpinfo、olsrv2info、netjsoninfo 等插件编入一个二进制。部署到资源较小的节点时,可以避免逐个处理动态库。
Zero 2 W 的本地编译速度较慢。可以在相同架构和 ABI 的 Pi 5 上构建后复制二进制,但必须先检查:
uname -m
file /usr/local/bin/olsrd2_static
ldd /usr/local/bin/olsrd2_static
aarch64 与 armv7l 不能混用。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。内部代价应从nhdpinfo、olsrv2info或 debug 日志观察。
FF DAT metric
当前配置使用 ff_dat_metric,它综合报文成功率和二层吞吐信息。源码中的 ffdat_loss_exponent 支持 linear、quadratic、cubic 和 dynamic。
将报文成功率记为 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²、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 netem 与 nftables 构造可重复的实验场景。
期望保留的好链路是:
node1 <-> node2
node2 <-> node3
node3 <-> node4
应恶化的非相邻直连关系是:
| 本地节点 | 恶化目标 |
|---|---|
| node1 | node3、node4 |
| node2 | node4 |
| node3 | node1 |
| node4 | node1、node2 |
数据面:tc netem
在 wlan0 根部安装 prio qdisc:默认流量进入干净队列 1:2,只有目的 MAC 匹配非相邻节点的帧进入带 netem 的 1: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 的链路质量较差。
应用与验证顺序
- 四台设备先清理全部旧
tc/nft规则。 - 验证无模拟时所有相邻和非相邻节点的基线 ping、吞吐和 OLSR metric。
- 每台设备使用正确的 ROLE、节点 IP 和真实
wlan0MAC 应用规则。 - 检查 qdisc/filter/nft counter,确认规则确实命中。
- 等待多个探测周期和拓扑收敛时间,再观察 route。
- 分别测试 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 和路由选择;内核日志用于检查 brcmfmac、cfg80211 和固件异常。两类日志需要分别检查。
第四层: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、iw、tc、nft 的控制边界变得复杂。
原因: 该程序需要直接操作宿主机无线设备、路由表和内核网络栈,容器无法提供明显的有效隔离。
处理: 运行时使用宿主机 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 的结果只有在影响到控制面采样后,才可能触发路由切换。