一句“为什么连不上 192.168.1.102”,我和 nl2sh 折腾出了一场局域网悬案
从 Ping 不通、ARP 全零、怀疑 AP 隔离,到目标设备反向 Ping 一次后突然恢复——这是一次真实的 nl2sh 端侧 Agent 网络排障记录。
项目:nl2sh — Natural Language to Shell
GitHub:https://github.com/nl2sh/nl2sh
有些网络问题最烦人的地方,不是“完全不通”。
完全不通其实很好查:网断了、IP 写错了、设备关机了,顺着链路一路摸过去就行。
真正折磨人的,是这种:
- A 连不上 C;
- B 却能连 C;
- C 又能反过来连 A;
- A 连局域网其他设备还基本正常;
- 路由器后台看起来也一切正常。
你盯着这些现象看十分钟,很容易开始怀疑人生,顺便怀疑路由器、手机、代理、VPN、频段、ARP、Android,最后怀疑是不是网线受到了量子纠缠。
这次我碰到的就是这么一个问题。
而整个排查过程,我没有在电脑上开 Wireshark,也没有来回adb shell、复制命令、粘日志、再把结果喂给大模型。
我直接在 Android 设备上运行nl2sh,然后只说了一句话:
“分析为什么连接不上 192.168.1.102”
接下来发生的事情,很像我旁边坐了一个会自己敲命令、看结果、推翻自己、继续查的网络工程师。
更重要的是——它就运行在出问题的那台 Android 设备上。
故事从一句最普通的话开始
当时 nl2sh 跑在一台 Android 手机上,手机处于家庭局域网里。
目标很简单:访问192.168.1.102。
但怎么都连不上。
于是我在 nl2sh 里输入:
我:分析为什么连接不上 192.168.1.102
nl2sh 没有先给我一篇《局域网故障排查大全》。
它直接开始看现场。
第一轮调用了 Android 网络状态、连接状态以及 Shell 探测工具,去确认:
- 当前 Wi-Fi 接口;
- 本机 IP;
- 子网;
- 路由;
- 网关;
- 目标主机的 Ping;
- ARP / Neighbor 状态。
很快拿到了第一批关键事实:
本机 IP:192.168.1.17/24 接口:wlan0 目标:192.168.1.102目标 Ping 不通。
但真正值得注意的不是“Ping 不通”,而是邻居解析失败:
192.168.1.102 dev wlan0 FAILED/proc/net/arp里更直白:
192.168.1.102 0x1 0x0 00:00:00:00:00:00 * wlan0也就是说,这时候问题甚至还没有走到 ICMP。
手机想给192.168.1.102发包,第一步必须先知道:
“192.168.1.102,你的 MAC 地址是多少?”
但这个 ARP 请求没有得到有效回应。
所以内核连二层目的地址都不知道,后面的 Ping 自然无从谈起。
这是第一个非常关键的判断:
不要把所有“Ping 不通”都当成同一种问题。
“ARP 都没解析出来”和“ARP 正常、ICMP 被丢”完全是两个排查方向。
如果只是 102 挂了,事情本来应该到此结束
第一轮结果很像一个普通故障:
.102可能关机;- IP 变了;
- Wi-Fi 掉了;
- 路由器后台租约没刷新;
- 或者设备网络栈有问题。
但我补了一条信息:
我:192.168.1.52 能连上 102。
这句话一下子改变了问题性质。
如果.52 → .102正常,那么:
.102显然不是简单关机;192.168.1.102这个地址也大概率确实有人在用;- “目标设备完全离线”的假设必须降级。
nl2sh 随后继续从手机本机去测试.52。
结果又出现了一个怪现象:
手机 -> 192.168.1.52:Ping 不通 手机 -> 192.168.1.102:ARP FAILED但是.52居然能访问.102。
此时一个很自然的怀疑冒出来了:
会不会是 AP Isolation / Client Isolation?
也就是路由器把无线客户端之间的通信隔离了。
这确实能解释一部分现象:手机能上网、能访问网关,但访问其他无线客户端异常。
于是排查开始往路由器和 Wi-Fi 频段方向走。
第一个“看起来很像”的答案:AP 隔离
当时手机连接的是 5GHz。
从网络信息能看到类似:
SSID: Becky_5G wlan0: 192.168.1.17/24这时我又补了一条:
我:52 也能连上 17。
也就是说:
.52 -> .17 能通 .17 -> .52 不通 .52 -> .102 能通 .17 -> .102 不通这已经不是简单的“双向隔离”了,而是明显带有非对称性。
nl2sh 继续检查手机上的 VPN、代理和网络状态,并发现设备上装有 Tailscale。
这又是一个非常合理的嫌疑人。
做 Android 网络问题排查的人应该都有这种经验:
一看到设备装了:
- Tailscale;
- Clash / Mihomo;
- VPN;
- 私有 DNS;
- 各种 tun;
第一反应通常就是:
“来,把这些全关了再说。”
nl2sh 也检查了这一层。
但当时并没有活跃的 Tailscale 隧道,Clash/代理也没有形成足以解释现象的证据。
于是 AP 隔离仍然是当时最像答案的方向。
我又补充:
我:手机和 52 都是连接 5GHz。
这让“一个在 2.4G、一个在 5G,所以跨频段不通”的简单解释也站不住了。
随后我从路由器后台拿到了 DHCP / 在线设备列表。
里面最重要的三台设备是:
OnePlus... 192.168.1.17 SSID5 LAPTOP... 192.168.1.52 SSID5 android 192.168.1.102 SSID1也就是说:
- 手机
.17:5GHz; - 笔记本
.52:5GHz; - 目标 Android
.102:2.4GHz。
看起来,“跨频段隔离”又重新变得可疑。
最直接的实验自然是:
把手机也切到 2.4GHz。
最有价值的一步:别争论,直接做实验
我把手机切到了 2.4GHz。
新的地址变成:
192.168.1.16nl2sh 确认 Wi-Fi 已切换后,立即重新测试.102。
结果:
192.168.1.16 -> 192.168.1.102 仍然失败ARP 还是:
00:00:00:00:00:00这一步非常重要。
因为它直接把“5GHz → 2.4GHz 的跨频段隔离”这个看似漂亮的解释打掉了。
这也是我很喜欢 Agent 实际跑在设备端的原因之一。
很多时候,大模型聊天排障最容易出现的问题是:
根据现象想出了一个挺合理的故事,然后一路把这个故事讲圆。
但真正的工程排障不是写小说。
猜想必须能被实验杀死。
切 2.4G 后仍然不通,就是一次非常干脆的反证。
我继续追问:是不是这台手机根本连不上任何局域网设备?
看到 2.4GHz 也不通,我问:
我:没有开 AP 隔离。手机是不是所有的局域网设备都连接不上?
这时 nl2sh 没有继续围着.102死磕,而是把问题扩大成了一个对照实验:
同一台手机,到底能不能访问局域网里的其他设备?
于是它对路由器后台里已知的多个地址逐一测试。
结果马上出现了非常有价值的分组:
| IP | 设备 | 接口 | 结果 |
|---|---|---|---|
.1 | 网关 | — | ✅ |
.3 | K155-11 | ONU1 | ✅ |
.9 | MiCO | SSID1 | ✅ |
.14 | IoT 设备 | SSID1 | ✅ |
.18 | DS220plus | LAN1 | ✅ |
.24 | Xiaodu | SSID1 | ⚠️ 部分响应 |
.69 | OPPO Pad | SSID5 | ⚠️ 部分响应 |
.21 | Android 手机 | SSID1 | ❌ Ping 不通,但有 MAC |
.52 | Windows 笔记本 | SSID5 | ❌ Ping 不通,但有 MAC |
.102 | Android 设备 | SSID1 | ❌ARP FAILED |
这个结果一下子又排掉了一个大方向:
手机的局域网能力没有整体坏掉。
它能访问:
- 网关;
- NAS;
- 多个 2.4GHz IoT;
- 甚至部分 5GHz 设备。
所以“手机自身把整个 LAN 都封了”“所有无线客户端都被路由器隔离”这些解释,都很难再成立。
而.102的异常仍然独一份:
别的设备至少能学到 MAC,只有
.102连 ARP 邻居项都一直失败。
到这里,问题已经从:
“为什么 Ping 不通?”
逐渐收敛成:
“为什么这台 Android 设备没有回应来自这台手机的邻居解析?”
这已经完全是另一个问题了。
全场最关键的一句话:102 能 Ping 通这台 16
接下来我说了一句真正改变结局的话:
我:102 能 ping 通这台 16。
这句话非常关键。
因为如果.102 → .16能主动 Ping:
.102此刻显然在线;.102的 Wi-Fi 不是完全断开;.102能向局域网主动发包;.16能收到.102发来的二层帧;- 那么
.16就有机会顺便学到.102的 MAC 地址。
nl2sh 马上去看ip neigh。
这一次,之前一直 FAILED 的.102发生了变化:
192.168.1.102 lladdr 08:38:69:2f:03:5f STALE再看/proc/net/arp:
192.168.1.102 0x1 0x2 08:38:69:2f:03:5f * wlan0这就是整场排障里最漂亮的证据。
在.102主动联系.16之前:
192.168.1.102 -> 00:00:00:00:00:00在.102主动发出流量之后:
192.168.1.102 -> 08:38:69:2f:03:5f邻居项出现了。
于是 nl2sh 做了最后一个非常简单的验证:
ping-c3-W1192.168.1.102结果:
64 bytes from 192.168.1.102: icmp_seq=1 ttl=64 time=328 ms 64 bytes from 192.168.1.102: icmp_seq=2 ttl=64 time=76.6 ms 3 packets transmitted, 2 received, 33% packet loss通了。
而且不是改了路由器配置,不是重启 Wi-Fi,不是关 VPN,也不是换频段。
只是.102主动发了一次流量,让.16获得了它的二层邻居信息,随后反向访问就恢复了。
真正能确定的根因是什么?
这里需要把“证据”和“推测机制”分开。
从这份日志里,我们可以非常确定地说:
1. 故障点在二层邻居解析阶段
最初.16/.17 → .102失败时,.102的邻居项是 FAILED,ARP 表里的 MAC 是全零。
这不是一个单纯的“ICMP 被防火墙丢掉”的现象。
2. 路由器 AP 隔离不是主因
因为:
- 手机可以访问大量其他局域网设备;
- 2.4GHz、5GHz 都做过实验;
- 用户确认没有开启 AP 隔离;
- 同 SSID 上也存在可访问设备。
3. 手机网络栈也不是整体故障
手机能正常访问网关、NAS 和多台 IoT,说明 wlan0、路由和本地 LAN 通信能力都在工作。
4..102主动产生流量后,问题立即改变
这是最核心的因果证据:
之前:.102 ARP FAILED ↓ .102 主动 ping .16 ↓ .16 邻居表出现 .102 的真实 MAC ↓ .16 再 ping .102 ↓ 成功所以最贴近现场的结论是:
.102在此前的无线省电 / 休眠 / 广播接收状态下,没有及时完成来自.16的 ARP 邻居解析;当.102主动产生网络流量后,设备与无线链路恢复到活跃状态,同时.16学到了它的 MAC,于是后续单播通信恢复。
这和“Android 设备处于深度休眠后网络行为发生变化”的方向是吻合的。
Android 官方文档明确说明,Doze 会限制应用的网络访问,并让设备尽量维持在休眠状态;AOSP 兼容性文档也明确存在 Wi-Fi Power Save 这一层无线省电机制。
但需要强调:
仅凭这份日志,不能把“某个具体 Android Doze 实现一定会丢弃所有 ARP 广播”写成普遍规律。
我们真正观测到的是:该设备在这个现场没有回应主动邻居解析,而主动发流量后问题消失。
这比一句“Android 睡眠会丢 ARP”更严谨,也更符合工程排障。
这次实战,我觉得 nl2sh 最有价值的不是“猜中了答案”
如果只看最后结果,很容易把这个故事总结成:
Android 睡着了,所以 Ping 不通。
但这恰恰会忽略整个过程里最有价值的东西。
nl2sh 真正让我觉得有意思的地方,是它把LLM 的分析能力和设备端真实执行能力接在了一起。
1. 它拿到的是“现场”,不是我转述的二手信息
传统的聊天式 AI 排障,经常是这种流程:
我:Ping 不通。 AI:执行 ip addr。 我:复制结果。 AI:再执行 ip neigh。 我:复制结果。 AI:再执行 dumpsys connectivity。 我:复制结果。 ……每一轮都要人工搬运现场信息。
而 nl2sh 本身就在 Android shell 里运行。
它可以直接调用结构化 Android 工具和 Shell,拿到:
wlan0;- 当前 IP;
- 路由;
- Wi-Fi 信息;
- Connectivity 状态;
/proc/net/arp;ip neigh;- Ping 结果;
- 本机安装的 VPN / 网络相关状态。
模型不是在“想象我的手机”,而是在看我的手机。
2. 它可以连续做对照实验
这次排查最有用的动作,不是任何一条神奇命令,而是不断做对照:
.17 -> .102 .17 -> .52 切到 2.4GHz .16 -> .102 .16 -> 网关 .16 -> NAS .16 -> 多台 IoT .102 -> .16 后再查邻居表 最后再次 .16 -> .102这已经不是“自然语言转成 Shell”这么简单。
它更像一个 Agent:
根据上一条工具结果,决定下一步该验证什么。
3. 它允许假设被证据推翻
这次中途其实出现了几次很像答案的方向:
- AP 隔离;
- 5G / 2.4G 跨频段隔离;
- Tailscale / VPN;
- 手机本地网络异常。
这些假设都不是胡猜,都有当时的现象支持。
但新实验一出来,该推翻就推翻。
尤其是:
切到 2.4G 后仍然不通。
这一刀直接砍掉了一个很诱人的错误故事。
这正是“Agent + 工具执行”比单轮问答更适合复杂排障的原因。
端侧 Agent 的优势,在这类问题里尤其明显
nl2sh 项目把 Android 原生adb shell作为一等运行环境。
它的核心程序是单个 Rust 可执行文件,可以直接推到 Android 设备运行;同时提供 TUI 和内置 Web 多会话界面,通过多轮 Tool Calling 把模型和本地工具连接起来。
这个架构放在网络、音频、系统状态、APK、Crash/ANR 这类设备问题里,有几个天然优势。
优势一:离故障现场足够近
你想排查“为什么这台 Android 连不上某个局域网设备”,最有价值的数据就在这台 Android 上。
如果 Agent 在云端,它永远缺最后一公里。
如果 Agent 就在设备的 shell 里:
现象 ↓ 模型判断 ↓ 本机执行 ↓ 真实结果 ↓ 模型继续判断闭环非常短。
优势二:自然语言可以直接变成诊断任务
这次我没有输入:
ipaddriprouteipneighcat/proc/net/arpping... dumpsys wifi...我说的是:
“分析为什么连接不上 192.168.1.102。”
我负责描述目标,Agent 负责选择手段。
这对那些“知道自己要查什么,但不想背一堆命令”的 Android 开发、系统开发、测试和运维人员尤其有价值。
优势三:安全边界仍然在设备端
这也是 nl2sh 目前设计里我比较看重的一点。
README 里明确写了:
- 默认多轮 Agent Tool Calling;
- 本地安全分类和确认;
balanced策略下只读操作可自动执行,修改操作需要确认,危险操作二次确认;- LLM 不能决定确认、风险等级、root 提升或超时;
- 用户编辑后的命令需要重新分类。
也就是说,LLM 可以建议“做什么”,但真正能不能做,由本地执行层决定。
对于一个能碰 Android shell、甚至可能跑 root 的 Agent,这比单纯“模型生成命令然后直接执行”重要得多。
这个案例也暴露了一个很真实的事实:Agent 不是神谕
这篇文章如果只把 nl2sh 写成“它一下就找到了根因”,反而不真实。
事实恰好相反。
它也会根据当前证据做出一个后来被推翻的判断。
比如 AP 隔离。
但我反而认为这更接近一个真正可用的工程工具。
网络问题本来就是贝叶斯式的:
看到证据 A → 提高假设 X 的概率 做实验 B → X 被否定 → 转向 Y 看到证据 C → 再缩小范围真正危险的不是“第一猜没猜中”。
真正危险的是:
第一猜出来后,就不再主动找反例。
一个有工具的 Agent,如果设计得好,应该不断问自己:
“我现在这个结论,有没有一条便宜的实验可以把它证伪?”
这次“切 2.4G 再试”“扫一遍其他 LAN 设备”“让.102反向 Ping 后立刻看邻居表”,其实都是这样的实验。
如果让我把这次排障压缩成一张流程图
整个过程大概是这样:
“为什么连不上 192.168.1.102?” │ ▼ 检查 wlan0 / IP / Route / Connectivity │ ▼ Ping .102 失败 │ ▼ ip neigh = FAILED ARP = 00:00:00:00:00:00 │ ▼ 发现 .52 可以访问 .102 │ ▼ 怀疑 AP 隔离 / 跨频段 / VPN │ ▼ 切换 2.4GHz 后仍失败 │ ▼ 扫描局域网其他设备 │ ├── 网关 / NAS / IoT 可以访问 │ └── .102 仍然 ARP FAILED │ ▼ 用户补充:.102 可以 Ping .16 │ ▼ 立刻检查邻居表 │ ▼ 发现 .102 MAC:08:38:69:2f:03:5f │ ▼ 再次 Ping .102 │ ▼ ✅ 通了真正的转折点,不是某个高级工具。
只是:
在正确的时机,看了一眼邻居表。
而“正确的时机”来自前面整条对话和实验链。
我为什么越来越喜欢把 AI 放到端侧做“手脚”
以前我理解的 AI 工具,更多是:
“你问,它答。”
现在我更在意的是另一种东西:
“你给目标,它在真实环境里观察、执行、验证,再回来告诉你发生了什么。”
尤其是 Android 这种环境。
我们日常调试经常要看:
dumpsys;- 网络;
- Audio;
- Camera;
- MediaStore;
- AppOps;
- 权限;
- ANR / tombstone;
- 存储;
- 温控;
- Doze;
- APK;
- UI 节点;
- 各种厂商定制状态。
命令并不是不会。
问题是:太多、太碎,而且真正的工作量在“下一步查什么”。
nl2sh 试图解决的,正是这一层。
不是让你彻底忘掉 Shell,而是让 Shell 从“人肉操作界面”变成 Agent 的执行能力。
目前的 nl2sh 能做什么?
以我写这篇文章时项目仓库的 README 为准,nl2sh 已经不只是最早那种“自然语言生成一条命令”的小工具了。
它现在更接近一个面向 Android shell 的 Agent Harness,包括:
- Android 原生 shell 作为一等运行环境;
- 单 Rust 可执行文件交付;
- TUI;
- 内置 Web 多会话界面;
- 多轮 Tool Calling;
- Shell 安全分类与审批;
- Android Connectivity / Wi-Fi / Doze / 权限 / 网络流量等结构化工具;
- Crash / ANR / 存储 / 温控功耗;
- APK 查看、DEX 类索引与单类反编译;
- 音频分析;
- Android UI 读取与交互;
- 文件工具;
- 可选 A2A / MCP 网关,让其他 Agent 调用设备上的 nl2sh。
如果你平时经常:
adb shell → 想命令 → 查资料 → 跑命令 → 看结果 → 再想下一条命令那这个项目应该会比较对胃口。
最后再回到 192.168.1.102
这次问题最后没有通过一条“万能修复命令”解决。
相反,它让我重新确认了一件很朴素的事:
排障最重要的不是命令有多高级,而是你能不能建立证据链。
一开始我们看到的是:
Ping 不通后来变成:
不是 Ping 的问题,是 ARP 邻居解析失败再后来发现:
不是整个局域网不通 不是单纯跨频段 不是简单 AP 隔离最后靠一个新事实:
.102能主动 Ping.16
把问题彻底收敛到邻居学习和目标设备无线活跃状态上。
然后,.102的 MAC 出现在邻居表里。
再 Ping。
通了。
这就是我理解的 Agent 排障价值:
不是替你背命令,而是陪你把一个“怪问题”,一点点变成一个可以被证明的问题。
写在最后
nl2sh 还在持续开发中,我自己也在不断拿真实 Android 场景去折腾它。
如果你也是 Android 开发、系统工程师、测试、运维,或者单纯喜欢折腾 ADB / Shell / Agent,欢迎试试:
GitHub:https://github.com/nl2sh/nl2sh
如果这个项目对你有一点帮助,欢迎顺手点个Star。Star 不只是数字,也能让我知道这种“把 Agent 真正塞进 Android 端侧”的方向有人需要。
也欢迎关注公众号。后面我会继续分享 nl2sh 的真实使用案例、Android 端侧 Agent、A2A / MCP、系统调试以及一些“本来只想查个小问题,最后挖出一条技术链”的实战记录。
如果你已经在用 nl2sh,或者有某个特别怪的 Android 问题想拿它试刀,也欢迎私信交流。
说不定下一篇文章,就是你的 Bug。
参考资料
- nl2sh GitHub:https://github.com/nl2sh/nl2sh
- Android Developers — Optimize for Doze and App Standby:https://developer.android.com/training/monitoring-device-state/doze-standby
- Android Open Source Project — Platform power management with Doze:https://source.android.com/docs/core/power/platform_mgmt
- 本文排障过程来自实际的 nl2sh Web 会话