用户通过SSH让Codex排查路由器,找回缺失的1 Gbps带宽
来源标题:Codex SSHd into my router and found an extra 1 Gbps I didn't know I was missing
一名用户用AI助手Codex通过SSH分析pfSense防火墙,发现其2 Gbps Orange宽带的有线测速仅约1 Gbps,最终在不更换硬件的情况下恢复到全速。调整涉及数据包队列、监控开销和CPU电源策略。原文未说明具体哪项改动起了决定作用。
以下正文由 AI 翻译自 Hacker News 最新(原文版权归原站所有),可能存在偏差,重要内容请以原文为准。
我本来想试试 OhMyPi。同时还有一个小谜团:我儿子那台有线连接的 PC,测速结果并不比我的 Mac 走 Wi-Fi 好多少。
这条宽带按理说是 2 Gbps。有线链路全程围绕 2.5GbE 搭建。但我测出来只有大约 1 Gbps。这个数字整齐得可疑,正是那种会让你开始怀疑网线的问题。
于是,作为我和 OhMyPi 的第一次互动,我让一个助手通过 SSH 检查防火墙。指令刻意定得很保守:先做只读分析;改动之前先提出方案并解释清楚。这是我家里的生产防火墙,不是可以随便折腾的虚拟机。
最终我们在不换硬件的前提下,把有线测速拉到了标称速率。有意思的是到底改了什么——以及这些测量结果能让我断言什么、不能断言什么。
链路是这样的:一台有线客户端、一台 2.5GbE 交换机、pfSense,以及一台连接 2 Gbps 宽带服务的 Orange Funbox。交换机到防火墙、防火墙到 Funbox 这两段链路,设计上都是跑 2.5 Gbps。
防火墙本身是裸机运行的 pfSense CE 2.8.1,CPU 为 Intel Celeron N4000,内存约 8 GB,网卡是 Intel I226-V,使用 FreeBSD 的 igc 驱动。WAN 是 igc0,LAN 是 igc1。
初步检查覆盖了链路协商、接口错误、PCIe 链路、路由、NAT、生效中的过滤与整形规则、网络内存、CPU 状态以及进程。一些有用的只读命令包括:
ifconfig igc0 ifconfig igc1 netstat -ibdn netstat -rn pfctl -si pfctl -sr pfctl -sn pfctl -sq netstat -m netstat -Q netstat -B
WAN 和 LAN 都报告:
media: Ethernet autoselect (2500Base-T <full-duplex>) status: active
这两个接口上没有 CRC 错误,没有接收溢出,没有网卡 watchdog 超时,没有生效的整形队列,也没有网络内存池耗尽的迹象。这并不能证明整条链路完美无缺,但至少排除了一个最简单的解释:防火墙的某个端口协商降到了千兆。
pfSense 的 WAN 是一个普通的 IPv4 以太网接口,网关指向 Funbox。pfSense 上没有 PPPoE 接口需要调优。网卡本身已经各有两个 RX/TX 队列,并启用了 MSI-X。这些我们都没有动。
2.5GbE 这个标签描述的是链路,不是转发性能基准。它本身并不能告诉你,CPU 和软件配置能撑住多少包处理量。
netstat -B 给出了一条有意思得多的线索:LAN 接口上挂着八个 bandwidthd 的 BPF 读取器。进程树显示有两组、每组四个运行中的进程,而且这些读取器的收包计数几乎一模一样。
FreeBSD 的 BPF 手册解释了为什么这值得注意:一个接口上的多个监听者看到的是同一个数据包流,各自套用自己的过滤器,接受某个包的监听者会拿到一份抓包副本。抓包和统计流量,会在转发之外额外增加工作量。
对这个发现,有两种容易夸大的方式。第一,八个读取器并不等于八倍的 CPU 开销或八份完整的数据包副本,过滤器和抓包长度都会影响实际开销。第二,多进程本身并不自动意味着 bug:BandwidthD 的源码本来就会按不同统计周期 fork 出工作进程。我们并没有查清这台机器上为什么会出现两组进程树。
一些 BPF 读取器还有丢包计数。那是抓包缓冲区的丢弃:监控应用漏掉了抓包记录。这并不能证明原始数据包没能到达互联网。同一个词,不同的层。
我停用了 bandwidthd。在同一阶段,我还调高了一个 IPv4 输入队列的上限。下一次测速结果就好了。因为我在那次测量之前同时改了两处,我没法诚实地把增益拆分到两者头上。抓包开销是重点嫌疑,但并不是单独测出来的某个百分比。
另一条线索在网络栈内部:
net.inet.ip.intr_queue_maxlen: 1000 net.inet.ip.intr_queue_drops: 32126
第一个值限制的是 IPv4 netisr 输入队列中等待的数据包数量。第二个是累计的丢弃计数器。它不是带宽设置,不是 TCP 接收窗口,也不是网卡的硬件接收环。
更准确地说,这个上限作用于每个 IPv4 工作流队列。这台机器上有两个工作流,每个 CPU 一个。FreeBSD 的实现会分别更新每个工作流的上限,再把它们的丢弃计数加起来作为协议层面的总数。它不是两个核可以共享借用的一份预算。
当一个数据包需要入队而队列没有空间时,入队路径会释放该数据包、增加该队列的丢弃计数,并返回 ENOBUFS。这是真正的输入队列丢弃,与漏掉的监控记录不同。
所以“CPU 在丢包”是一种可以理解的简略说法,但准确的说法是:内核因为某个 IPv4 输入队列已满而拒绝了数据包。单看这个计数器,我们无法判断是哪种流量造成的,也无法判断底层压力是一次短突发、调度问题,还是持续的处理瓶颈。
最初的 netstat -Q 输出显示,两个队列都已经顶到了配置上限。按照 Netgate 的输入队列排障指南,我把上限翻了一倍:
sysctl net.inet.ip.intr_queue_maxlen=2000
这样每个 IPv4 输入队列就能容纳 2,000 个等待的数据包。它并不会让任何一个核执行指令更快。
即使以太网链路还有充足的标称带宽,一个满队列也可能丢弃 IP 数据包。对一条 TCP 数据流来说,数据缺失会带来两项代价:发送方可能需要重传那些字节,而它的拥塞控制响应可能减少它在途的数据量。
TCP 提供的是可靠、有序的字节流。它在传输层修复缺失的数据。它不会因为某个报文段丢了就重跑某个应用函数,或者重放某个 HTTP 请求。应用层的重试是另一回事。
(原文较长,此处译出前 6000 字,完整内容请点「打开原文」。)
正文由 探How 从来源站点 RSS 同步,内容未经改写;遇到排版缺失或需要图片、视频时请以原文为准。