【免费下载链接】owly
airdrop for non-apple devices
owly-radio 是 Owly 项目中最贴近硬件的一层:它把一块普通的 Linux Wi-Fi 网卡变成一条可承载 AWDL 的awdl0IPv6 链路,从而不必依赖 Apple 的 Broadcom 芯片。本文以该 crate 的 README 为骨架,逐条展开Backendtrait、三种注入模式、无 root 能力探测、drivers.toml怪癖表、/etc/owly/radio.toml配置项与崩溃恢复日志,并结合源码与测试说明每一步实际发生了什么。
owly-radio 在 Owly 中的位置
README 开篇给出的定位非常明确:AWDL 是 AirDrop 所依赖的 Wi-Fi 链路,而 owly-radio 负责让它在普通 Linux 网卡上运行,底层通过 nl80211 驱动 owly-awdl,并且只支持 Linux。
它在整个栈中的角色可以从 crate 根文档看清:
crates/owly-radio/src/lib.rs radio —— Backend trait 与它交换的类型(owlyd 运行的那一层) injection —— 用 owly-awdl 实现 Backend,通过 nl80211/rtnl 建接口 native —— owly-awdl 的 driver 在 monitor 接口上的实现 probe —— 不需要 root 的能力探测 quirks —— 探测不出来的、按驱动记录的经验 config —— 读 /etc/owly/radio.toml journal —— 每次改动前的恢复日志 fake —— 给上层做测试用的假后端对应到部署形态:owlyd(Owly 的系统服务)持有这块射频,通过 D-Bus 把链路租给上层;AirDrop 本身(owly receive或 omdrop)以普通用户身份在awdl0上收发,就像一个普通网络接口。README 用一句话概括了这个分层:"owlyd, Owly's system service, runs it and lends the link out over D-Bus." 参见 packaging/owlyd.service。
核心抽象:radio::Backend与Link
三个方法定义的射频生命周期
radio.rs 中的 trait 极小,但每个方法都对应一个真实的运维语义:
pub trait Backend: Send { /// Brings the link up. fn up(&mut self) -> impl Future<Output = Result<Link, Error>> + Send; /// Takes the link down and gives Wi-Fi back. Safe to call when it's already down. fn down(&mut self) -> impl Future<Output = Result<(), Error>> + Send; /// Undoes what a session left behind when the process running it died. fn recover(&mut self) -> impl Future<Output = Result<(), Error>> + Send; }三点值得注意:
up不只是"打开网卡":它要建出awdl0TAP 接口、配上 IPv6 link-local 地址,并开始收集链路上听到的 AWDL 对端。down是幂等的:文档明确写了"already down 时调用也安全",这一点在 injection/mod.rs 中由self.live.take()保证——没有活动会话时只是重放一次空的日志。recover是崩溃后的补救:服务启动时调用一次,把上一个进程死在半路留下的改动撤销掉;没有残留时什么也不做。
Link:上层能看到的全部
up成功后返回的Link是上层唯一可见的"射频事实"(radio.rs):
| 字段 | 类型 | 含义 |
|---|---|---|
name | String | 固定为awdl0;对 socket 而言只有 scope ID 有意义(NET-2) |
index | u32 | 接口索引 |
mac | MacAddr | AWDL 地址,整个会话期间固定(AWDL-2) |
addr | Ipv6Addr | 由mac推导出的 link-local 地址(NET-1) |
announce | Option<Announce> | 网卡本身不承载 mDNS 记录时,接收方重复通告的节奏(MDNS-7) |
peers | watch::Receiver<Vec<PeerInfo>> | 当前有效的 AWDL 对端 |
state | watch::Receiver<LinkState> | Up或Lost(String) |
LinkState::Lost是"射频自己没了"(网卡或主机接口故障),与"被主动down掉"是两回事——tests/injection.rs 里专门断言了主动关闭后状态仍是Up。
Announce:两种模式的两套节奏
Announce的两个常量不是随意取值,而是与信道的驻留方式绑定(radio.rs):
| 常量 | count | interval | pause | 适用场景 |
|---|---|---|---|---|
Announce::SHARED | 3 | 50 ms | 3 s | 共享 AP 信道;组播走 AWDL 的组播时隙,手机在那几个时隙处于我们的信道 |
Announce::FIXED | 64 | 16.4 ms(16 TU) | 3 s | 固定单信道(AWDL-12);手机每 16 个时隙只来访一两个,需要在一整个周期内按可用性窗口连发 |
owly radio up会把这组参数直接打印给用户,例如"receivers on it announce 3 times 50 ms apart, every 3 s (MDNS-7)",见 owly-cli/src/radio.rs。
失败语义:NotNow与NoRadio
错误类型只有两种 kind(radio.rs):
ErrorKind::NotNow:现在不行,稍后再试可能就好(比如 Wi-Fi 还没连上、射频忙);ErrorKind::NoRadio:没有可用的射频,或者它失败了。
这套划分一路透传到 CLI:owly radio up遇到NotNow/Busy退出码为 3,其余为 4(owly-cli/src/radio.rs)。
injection::Injection:三种模式与真实的下发顺序
injection是Backend的主体实现。它把 AWDL 放在一个注入式 monitor 接口上,按网卡能力选择三种模式之一(injection/mod.rs):
| 模式 | Wi-Fi 状态 | 承载方式 | 谁 ACK 手机的单播 |
|---|---|---|---|
| Shared | 保持连接 | monitor 接口使用 station 的 MAC,AWDL 跑在 AP 的信道上,只通告手机在我们信道上的时隙 | station 自己 |
| Exclusive | 会话期间断开 | 用 AWDL BSSID 上的 ad-hoc 小区顶替 station(无 ad-hoc 能力的卡改用隐藏 AP),并继承其 MAC | 小区 / 隐藏 AP |
| Active monitor | 会话期间断开 | 一个会为自己地址回 ACK 的activemonitor 顶替 station | active monitor 自身 |
模式本身不来自运行时猜测,而是来自 quirks 表(下一节);probe只负责校验"这张卡跑不跑得动这个模式"。
接口命名与常量
injection/mod.rs 定义了会话中会出现的接口名,排查问题时非常有用:
awdl0:AWDL 数据面(TAP,MTU 1450);owlymon0:注入/抓包用的 monitor(MTU 请求 8192,以便容纳 A-MSDU);owlyibss0:独占模式的 ad-hoc 小区;owlyap0:无 ad-hoc 模式时的隐藏 AP。
共享模式:先把 station 等稳
共享模式的第一件事不是改配置,而是确认 Wi-Fi 真的通了。shared()会以 1 秒间隔最多尝试 15 次,要求同时拿到链路频率且 IPv4 网关可达(injection/setup.rs):
let mut freq = None; for _ in 0..15 { freq = ops.link_freq(&name); if freq.is_some() && ops.gateway_reachable(&name) { break; } freq = None; ops.sleep(Duration::from_secs(1)); }拿到频率后必须落在 AWDL 的社交信道(6 / 44 / 149)上,否则返回NotNow并给出"把路由器换到这三个信道之一"的建议(文案见 probe.rs)。
随后的改动顺序在测试中被逐条断言(tests/injection.rs):
write /sys/kernel/debug/ieee80211/phy0/mt76/runtime-pm 0 write .../deep-sleep 0 add phy0 owlymon0 monitor mac owlymon0 <station MAC> up owlymon0而日志中先记录的是Reconnect(若该驱动需要)与Mt76,再记录Interface——因为拆除时是倒序执行的,这样能保证先删 monitor、再恢复电源管理、最后重连 station(injection/setup.rs 的注释写明了这个顺序理由)。
独占模式:ad-hoc 小区、隐藏 AP 与"station 自己变成小区"
exclusive()同时服务Exclusive与ActiveMonitor两种模式(injection/setup.rs)。它的关键步骤是:
- 在 station 仍连着时读出 NetworkManager 的连接 UUID(拆完要还原);
- 记录
Step::Station { station, connection }; nmcli device set <sta> managed no,再把接口 down 掉,否则 NetworkManager 会立刻把它拉回来;- 如需关闭 mt76 电源管理,先记录再写;
- 申请监管域(若配置了
country); - 按 quirks/配置给定的顺序挑一个"允许发射"的信道;
- 起小区(ad-hoc 或隐藏 AP),再起 monitor;
- 以
Presence::Fixed启动 AWDL。
其中"小区"的实现有三种分支,来自同一段代码:
- 有 ad-hoc 能力:创建
owlyibss0,用join_ibss加入 AWDL 的 BSSID,HT 小区(因为 iPhone 用 HT 速率发单播,非 HT 小区既收不到也不会 ACK); - 驱动拒绝在 station 旁边再建 ad-hoc 接口(部分 AX211):把 station 自己改成 IBSS,并记录
Step::Ipv6与Step::StationType以便还原; - 完全没有 ad-hoc 能力:写一份 hostapd 配置,跑一个隐藏 AP。
隐藏 AP 的配置是"存在但没人会连"的设计(injection/setup.rs):
interface=owlyap0 driver=nl80211 ssid=owly-<4 字节随机 hex> ignore_broadcast_ssid=1 hw_mode=a # channel 6 时为 g channel=<6|44|149> ieee80211n=1 beacon_int=1000 # 而非小区的 10000,避免驱动阻塞启停 wpa=2 wpa_key_mgmt=WPA-PSK rsn_pairwise=CCMP wpa_passphrase=<16 字节随机 hex>文件以0600写入运行目录,日志写到hostapd.log。之所以用 1000 TU 而不是小区惯用的 10000 TU,源码注释给了明确理由:RTL8852BU 的驱动会为 beacon 阻塞 AP 的启停,10 秒 beacon 会让启动耗时 21.6 秒、停止耗时 15 秒。
Active monitor:先让 station 变成被动 monitor
active_monitor()里有一个容易忽略的细节:只把 active monitor 调信道往往无效(MT7612U、内核 7.2 上会保持旧信道),所以代码先把 station 改成被动 monitor 并同样调谐,为 mac80211 建立信道上下文,然后再建 active monitor 并调谐(injection/setup.rs)。测试对这条调用序列逐行做了断言(tests/injection.rs)。
监管域:只在独占类模式下申请
regdom()的行为有明确边界(injection/setup.rs):
- 只有配置了
country才动作;共享模式沿用 AP 的信道,不申请; - 驱动自己管理监管域时(Intel 的 LAR)跳过,因为请求会被忽略;
- 申请前记录原值(
Step::Regdom),申请后最多等 5 秒让内核生效,然后重新读取 wiphy 以获得可发射信道列表。
这一点与drivers.toml里 Intel 的记录相互印证:"With the station down the firmware forgets its country and is back on the world's regulatory domain, where all of 5 GHz is No IR"。
probe:不需要 root、不改变任何东西
README 强调 "probefinds what the card can do, without root"。这直接决定了owly doctor和 owlyd 的Probe()可以被任何用户调用(probe.rs)。
选卡逻辑
find_station()的优先级是(probe.rs):
/etc/owly/radio.toml中station指定的接口(存在才用);- 否则取第一个
managed类型的 Wi-Fi 接口,且排除awdl0、owlymon0、owlyibss0、owlyap0这些自家接口; - 都找不到 →
missing中记一条hardware。
随后按 quirks 表判定状态:tested直接通过;untested与broken除非allow_untested = true,否则拒绝,并在文案里带上驱动/设备 ID 与"如何放行"的说明。
报告的结构
Capabilities(radio.rs)同时承担机器可读与人类可读两个用途:
| 字段 | 含义 |
|---|---|
station/driver/device | 用哪块卡、什么驱动、什么 PCI/USB ID |
status/mode/channel | quirks 给出的结论与会话信道 |
card | nl80211 报告的能力(见下) |
hardware | 这台机器是否存在可驱动的射频 |
missing | 阻塞项,按"最根本在前"排序,id为hardware/backend/radio |
issues | 能用但不够好的地方(卡的已知问题 + 系统配置问题) |
Card里的字段几乎就是排障清单:ibss、ap、active_monitor、monitor_beside_station、ibss_beside_station、self_managed_reg、regdom,以及三个社交信道各自的transmit与why(absent/disabled/no-ir/radar)。
missing的三分法值得记住,它决定了问题该找谁:
hardware:卡不行,换硬件或放行 untested;backend:本机的文件/工具缺失(如没装 hostapd、读不到 nl80211);radio:卡的状态,用户可以自行改变(Wi-Fi 没连、信道不对、监管域为00)。
两条容易被忽略的检查
Windows/防火墙:probe会读/etc/ufw/ufw.conf,发现ENABLED=yes就提示:除非放行awdl0上来自fe80::/10的 TCP 8771,否则接收端被挡、手机永远看不到这台电脑(probe.rs,对应 NET-4)。
信道 6 的代价:独占类模式如果最终落在 6,报告里会追加一条 issue:"AirDrop 在这台机器上跑 6 信道,只能拿到 iPhone 十六个时隙里的一两个,传输很慢"(probe.rs)。这条文案与drivers.toml的实测记录一致,属于项目自身的观测结论。
quirks与drivers.toml:探测不到的经验
README 说得很准确:"quirksholds what the probe can't detect, per driver"。nl80211 能告诉你卡支持哪些接口类型,但告诉不了你"这块卡的固件在删掉 monitor 之后不再收包"。
加载与覆盖规则
quirks.rs 的实现:
- 先用
include_str!("../drivers.toml")载入随 crate 打包的表(PACKAGED); - 若
/etc/owly/drivers.toml(OVERRIDES)存在,解析后按键整体覆盖; - 必须存在
"*"兜底项,否则报错。
查找优先级是设备 ID > 驱动名 >"*",并由Source枚举标注来源(Device/Driver/Fallback)。测试里用 MT7927 说明了为什么需要设备级条目:它跑在 MT7925 的驱动上,但行为并不相同。
字段速查
[drivers.<name>]与[devices."<pci-or-usb-id>"]共用同一套字段:
| 字段 | 默认值 | 作用 |
|---|---|---|
status | 必填 | tested/untested/broken |
mode | 必填 | shared/exclusive/active-monitor |
reconnect_after_monitor | false | 删掉 monitor 后强制重连 station(有些驱动之后不再收包) |
mt76_pm_off | false | 会话期间关闭 mt76 运行时电源管理 |
channels | [44, 149, 6] | 独占类模式尝试的顺序;共享模式用 AP 的 |
rx_ampdu | false | 接受手机的 Block Ack 会话(AWDL-15),要求硬件能自己回 Block Ack |
tx_noack | 未设 | 单播不等硬件 ACK;未设时由模式决定(固定信道为 true,共享为 false) |
supports_tx_ldpc | false | 驱动能对注入帧做 LDPC 编码(owly-awdl 尚未使用) |
issues | [] | 一句话一条的已知问题,probe会展示 |
notes | "" | 硬件与观测记录,写给人看 |
校验只有两条硬约束(quirks.rs):信道不得超出[6, 44, 149],且列表不能为空。此外serde(deny_unknown_fields)会让写成reconect_after_monitor这类拼写错误直接报错,而不是被静默忽略——这一点在测试里被专门覆盖。
表里都有什么
按当前打包的表,可以按模式归类(详细说明见 crates/owly-radio/drivers.toml):
- shared(共享 AP 信道):
mt7925e(tested,附带"发送约 490 kB/s、接收约 1.3 MB/s"与"删 monitor 后停止收包"两条 issue)、rtw89_8852ae/rtw89_8852be/rtw89_8852ce(untested); - exclusive(ad-hoc 小区或隐藏 AP):
iwlwifi(tested,固定channels = [6])、rtw88_8822be(tested)、rtw89_8852bu(untested,无 ad-hoc,走隐藏 AP); - active-monitor:
mt76x2u(tested,channels = [149, 44, 6]、rx_ampdu = true、supports_tx_ldpc = true)、mt7921e(untested,channels = [6]); - broken:
14c3:7927、8086:095a、8086:24f3、8086:24fd、rtw88_8821ce、ath11k_pci、ath12k_wifi7_pci; - 兜底
"*":untested+shared+reconnect_after_monitor = true,即"任何其他卡,需要能在关联状态下做 monitor 注入,且 AP 在 6/44/149"。
这张表不是拍脑袋写出来的,notes里记录了具体机型、内核版本与观测结果。引用这些内容时应把它们当作项目维护者记录的实测结论,而非普遍保证。
/etc/owly/radio.toml:本机怎么跑
README 用一行带过 "configreads/etc/owly/radio.toml",实际字段与校验都在 config.rs。完整示例(取自该文件文档注释,并补上语义):
station = "wlan0" # 要用的 Wi-Fi 接口;不写则取第一个 managed 接口 allow_untested = false # 是否允许在 quirks 表未标记 tested 的卡上运行 reconnect_after = true # 覆盖驱动的 reconnect_after_monitor country = "DE" # 独占类模式:申请哪个监管域 channels = [6] # 独占类模式:覆盖驱动的信道顺序 aggregation = true # 覆盖驱动的 rx_ampdu:接受手机的 Block Ack 会话 tx_rate = "mcs7" # 数据帧速率 hostapd = "/usr/sbin/hostapd" # 无 ad-hoc 模式时隐藏 AP 用;不写则在 PATH 中找 tx_amsdu = true # 对端接受时用 A-MSDU 发单播每个键的边界条件
station:只在接口确实存在时采用;给了名字但不存在会直接报"你设置的接口不存在"。allow_untested:布尔,默认false;untested与broken都靠它放行(broken的用途是复测)。country:必须是两位大写字母或数字(如DE),"de"会被拒绝并给出提示;校验代码见 config.rs。它在内核监管域为00(世界域)时尤其重要——世界域下 5 GHz 基本不可发射,独占模式会先申请再等内核生效,然后才挑信道。channels:元素必须属于{6, 44, 149},[6, 36]会报错;顺序即尝试顺序。aggregation:覆盖 quirks 的rx_ampdu;在 tests/injection.rs 中验证了设为false时accept_addba关闭。tx_rate:接受6/9/12/18/24/36/48/54(Mbit/s)或mcs0–mcs15,可带-sgi后缀;写错会得到一条列出合法取值的错误。hostapd:默认值并非空,而是由injection::hostapd()解析——先看构建期OWLY_HOSTAPD,再在PATH与/usr/bin、/usr/sbin、/sbin中查找(injection/mod.rs)。tx_amsdu:布尔,默认false。
未写文件 = 全默认
Config::load对NotFound直接返回Config::default(),其它 IO 错误与 TOML 解析错误才上报(config.rs)。结构体上的deny_unknown_fields意味着拼错的键(例如把station写成stations)会立刻失败,而不是被忽略——这样配置文件不会"看起来生效了其实没有"。
恢复日志:为什么杀掉 owlyd 也不会丢 Wi-Fi
这是整个 crate 里最容易被低估、但在实际使用中最关键的设计。README 只用一句描述recover:"undoes what a session left behind when its process died, from a journal"。
先写日志,再做改动
journal.rs 的约定是:每一处对内核和 NetworkManager 的改动,都在执行前写入日志。日志是 JSON,每次改动整体替换,位于运行目录(owlyd 为/run/owly),进程结束仍在,重启后自然消失——因为改动本身也不跨重启。
步骤类型(Step)按"如何撤销"来设计:
Step | 撤销动作 |
|---|---|
Process { what, process } | 停止会话启动的进程(hostapd):先 SIGTERM,宽限 2 秒后 SIGKILL |
Interface { name } | 先leave_ibss(若为 IBSS),再 down、删除接口 |
Reconnect { station } | 若确实删过接口,等 1 秒后强制 station 重新关联 |
Station { station, connection } | 接口 up、交回 NetworkManager,并按 UUID 恢复原连接 |
StationType { station } | 把被改成 IBSS/monitor 的 station 改回 managed |
Ipv6 { iface, disable_ipv6 } | 恢复net/ipv6/conf/<iface>/disable_ipv6 |
Mt76 { dir, runtime_pm, deep_sleep } | 恢复 mt76 的电源管理两个文件 |
Regdom { alpha2 } | 恢复原监管域 |
撤销时从最新一条开始(while let Some(step) = self.steps.last()),每撤销一条就从日志里删掉并重新落盘;只有"后续步骤依赖、却失败的步骤"才会中止并保留剩余部分(journal.rs)。
单所有者:flock与 "The radio is busy"
日志用Flock::lock(..., LockExclusiveNonblock)独占锁住journal.lock(journal.rs)。拿不到锁时映射为WouldBlock,再被上层转成Error::not_now("The radio is busy: ...")(injection/mod.rs)。测试one_owner_of_the_radio_at_a_time断言第二个后端的up()得到NotNow且消息含 "busy"。
运行目录里还有什么
rundir.rs 列出了/run/owly的内容:
| 文件 | 内容 |
|---|---|
journal.json/journal.lock | 恢复日志与它的锁 |
hostapd.log | hostapd 输出 |
hostapd.conf | 隐藏 AP 配置,仅 root 可读 |
写入采用"临时文件 +sync_all+rename",因此读取者永远不会看到写了一半的文件;这一点在files_are_replaced_whole测试里被验证。
一个实用推论:排查"Wi-Fi 没回来"时,先看/run/owly/journal.json。如果它非空,说明上一次会话还有未撤销的步骤;下一次owlyd启动(或recover())会继续处理。对应测试a_dead_owners_session_is_undone_from_the_journal模拟了"后端被 drop 掉"的情形,并断言 monitor/IBSS 消失、station 交回 NetworkManager、日志清空(tests/injection.rs)。
native:awdl0与 monitor 上真正发生了什么
README 只提到"runs owly-awdl over nl80211",细节在 native.rs:
Air实现在 monitor 接口上的 packet socket(AF_PACKET/SOCK_RAW,ETH_P_ALL),收包时解析 radiotap 取出帧与 RSSI,发包时拼一个 radiotap 头再写;Netif实现是awdl0这块 TAP:无 packet info 头、关闭 offload,MTU 1450,MAC 设为 AWDL 地址(AWDL-2),并添加 EUI-64 的 link-local 地址且不做 DAD(NET-1);- 每个转为有效的对端会得到一条永久邻居表项(NET-2),对端失效时删除;
- monitor 接口请求 MTU 8192,以便容纳 A-MSDU;实际生效值会从 sysfs 读回,并据此计算
max_frame。
如果awdl0的 MAC 被系统改写(例如 udev 的MACAddressPolicy),上层会记一条警告,因为对端是按 AWDL 地址寻址的(injection/mod.rs)。
fake:给上层用的假后端
README 说 "fakeis a backend for testing what runs on top"。它没有射频,只在某个根目录下伪造sys/class/net/awdl0与proc/net/if_inet6的一行,并通过<root>/owly-fake/下的文件遥控行为(fake.rs):
| 文件 | 效果 |
|---|---|
up.fail | up失败,格式3 <句子>(NotNow)或4 <句子>(NoRadio) |
up.sleep | up先等待这么多秒 |
peers | 每行一个 MAC,作为链路的对端 |
lose | 文件出现即表示链路丢失 |
calls | 记录up/down/recover,每行一次 |
固定常量MAC = 02:00:00:00:00:01、INDEX = 99,probe()返回一块"shared 模式、44 信道、就绪"的假卡。对测试 owlyd 的 D-Bus API 与 omdrop 钩子来说,这比真卡可控得多。
权限、依赖与运行方式
README 的这段是部署要点,逐条展开如下:
Bringing the radio up needs
CAP_NET_ADMINandCAP_NET_RAW, and runs NetworkManager'snmcli,ip,pingand, on cards without ad-hoc mode,hostapd.
对应到实现:
- 能力:
nl80211(建/删/改接口、设信道、join/leave IBSS、设监管域)、rtnetlink(MAC、MTU、up/down、邻居)、packet socket 与 TAP 都需要上述两个能力;owlyd 的 unit 也正是只授予这两个(packaging/owlyd.service)。 - 外部命令:
nmcli用于device set ... managed、读连接 UUID、connection up、device connect;ip -4 route与ping用于判断网关是否可达;hostapd仅在无 ad-hoc 能力时使用。 - debugfs:mt76 的电源管理在 debugfs 下;owlyd 没有
CAP_SYS_ADMIN,所以 unit 里声明Wants=sys-kernel-debug.mount,由 systemd 先挂载。 /run/owly:由RuntimeDirectory=owly+RuntimeDirectoryPreserve=yes提供,保证崩溃后日志还在。
命令行入口
owly doctor:调用 owlyd 的Probe();连不上 owlyd 时退化为本地probe::from_config,并在报告最前面插入一条"owlyd 不可达、这是本地报告"的missing(owly-cli/src/doctor.rs)。missing为空时退出码 0,否则 1。owly radio up [--for SECONDS]:从 owlyd 取链路并持有,直到 Ctrl-C / SIGTERM / 到时长 / 链路结束;期间打印对端变化,结束时释放(owly-cli/src/radio.rs)。owly radio peers:列出当前会话的 AWDL 对端;链路未 up 时退出码 4。
owly doctor的文本排版(字段宽度、card行内容、channels的ok/no-ir标记)可参考 doctor.rs 与它的单元测试期望:
station wlan0 (mt7925e, 14c3:0717), tested mode shared, channel 44 card phy0: ad-hoc yes, AP yes, active monitor no, monitor beside the station yes regdom DE channels 6 ok, 149 no-ir ready note: Slow.为新卡调参的实操路径
主 README 已经说明/etc/owly/drivers.toml与打包表同格式、可以直接覆盖,因此不必重新编译就能试新卡。推荐顺序:
owly doctor确认卡被识别、卡住了哪一项(hardware/backend/radio);- 若是"未测试"或"已知不工作",先在
/etc/owly/radio.toml设allow_untested = true复测; - 若是信道/监管域问题,设
country(独占类模式)或把路由器换到 6/44/149(共享模式); - 若要改模式或信道顺序,在
/etc/owly/drivers.toml覆盖对应条目。注意覆盖是整条替换,不是字段合并:测试overrides_replace_whole_entries表明只写status/mode/rx_ampdu时,reconnect_after_monitor会回落到false、channels回落到默认值、notes变空。所以覆盖时最好把需要的字段一次写全。
# /etc/owly/drivers.toml [drivers.mt7925e] status = "tested" mode = "shared" reconnect_after_monitor = true mt76_pm_off = true rx_ampdu = true channels = [44, 149, 6]共享模式若想调整发送行为,可在/etc/owly/radio.toml里设aggregation、tx_rate、tx_amsdu;独占类模式再考虑channels与country。
测试与验证:三个层次
README 没有展开测试,但仓库给出了三档可复现的验证手段,正好对应"不碰硬件 → 碰内核 → 碰模拟射频":
crates/owly-radio/tests/injection.rs:用假的Ops实现 + 替身 AWDL + 假 hostapd 脚本,断言每种模式的调用顺序、日志内容、启动参数(信道、Presence::Share/Fixed、no_ack、accept_addba、速率)、拆除顺序、失败回滚,以及"进程死掉后由下一个所有者撤销"。crates/owly-radio/tests/native.rs:在 user + net namespace 里用 veth 代替空气,让Native与一个模拟 iPhone 的owly_awdl::Node对话,验证awdl0的 MAC/MTU/无 DAD 的 link-local、邻居表项、内核经 TAP 收发 IPv6。无需 root、无需真卡。crates/owly-radio/tests/hwsim.rs:在mac80211_hwsim上把三种模式各跑一遍,并让两台模拟电台互相听见、互 pingawdl0。它需要 root、iw/ip/ping,共享模式还需要hostapd;为不打扰宿主 Wi-Fi,三张模拟卡被放进各自的 network namespace。入口是:
just hwsim # 等价于: cargo test --locked -p owly-radio --test hwsim \ --config "target.'cfg(all())'.runner = 'sudo OWLY_HWSIM=1'" -- --nocapture未设置OWLY_HWSIM=1时该测试直接通过而不做事;内核的 hwsim 不提供 active monitor 时,该模式会被跳过并打印原因。
状态与边界
README 的 Status 一节只有一句,但很重要:0.x 阶段,API 可能在小版本之间变化,直到 Owly 1.0。许可证为 MIT。
需要明确的边界条件:
- 仅 Linux(依赖 nl80211 / rtnetlink / TAP / debugfs);
- 拉起射频需要
CAP_NET_ADMIN与CAP_NET_RAW,因此常规用法是交给 owlyd,而不是普通用户直接调用后端; - 共享模式要求 Wi-Fi 已连接且 AP 在 6/44/149;独占类模式要求至少有一个可发射的社交信道;
- 无 ad-hoc 能力的卡必须有
hostapd才能走隐藏 AP; - 具体某块卡能不能跑、跑哪个模式,以
drivers.toml与owly doctor的实际输出为准——本文不对此做超出该表的推断。
小结
owly-radio 的价值在于把一个高度硬件相关、容易把系统网络搞坏的操作,收敛成三个方法(up/down/recover)和一份可撤销的日志:
- 接口稳定:
Backend之上只看到awdl0与对端列表,上层无需关心是 ad-hoc、隐藏 AP 还是 active monitor; - 决策有据:模式与参数来自
probe(可探测)+quirks(经验),两者分离,便于社区用/etc/owly/drivers.toml增量贡献; - 失败可逆:先写日志再动手,进程被杀也能在下次启动时把 Wi-Fi 还回去,并用
flock保证同一时刻只有一个所有者。
若你正在适配一块新网卡,建议的阅读顺序是:crates/owly-radio/README.md → crates/owly-radio/drivers.toml → crates/owly-radio/src/probe.rs → crates/owly-radio/src/injection/setup.rs,再对照 crates/owly-radio/tests/injection.rs 观察每个模式应有的调用顺序。
【免费下载链接】owly
airdrop for non-apple devices
相关推荐
owly-awdl 深度解析:用 sans-IO 状态机实现 AirDrop 的 AWDL 链路
owly awdl 深度解析:用 sans IO 状态机实现 AirDrop 的 AWDL 链路 owly awdl 是 Owly 项目中的 AWDL(Appl
Ryujinx 模拟器打不开、掉帧、黑屏?按症状自查的故障排除指南
Ryujinx 模拟器打不开、掉帧、黑屏?按症状自查的故障排除指南 Ryujinx 是一款用 C 编写的实验性 Nintendo Switch 模拟器。如果你的
硬件仿真图形学agent-memory 配置层 ConfigSource 深度解析:六类参数的运行时动态配置抽象
agent memory 配置层 ConfigSource 深度解析:六类参数的运行时动态配置抽象 导读 :本文基于 agent memory(openJiuw
人工智能Agent 记忆AI AgentRAGMCP 服务
创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考