TUN/TAP:无效参数 — OpenVPN 帧格式不一致
代码 22 的写入错误通常意味着两端对数据包的帧格式没有达成一致,而不是驱动故障。先比较两端的压缩设置,再看设备类型。
客户端认证通过,日志显示已连接,但没有一个数据包穿过隧道。服务器以 write to TUN/TAP: Invalid argument (code=22) 结束尝试,这看起来像驱动故障,但通常不是。在路由器和小型 Linux 服务器上,这一行往往意味着两端对数据包的帧格式没有达成一致,于是内核拒绝交给它的已解密缓冲区。此时路由还没有被测试。
正确的路由表和错误的帧格式约定,从客户端看完全一样:已连接,但什么都不通。
隧道起来了,却没有数据通过
隧道设备上的写入错误发生在解密之后、路由之前。守护进程解密一个包,交给接口,接口却拒绝了它,因为缓冲区与该设备期望的不符。代码 22 是无效参数的通用回答,所以有用的信息是层次:这是数据路径,而不是握手。如果流量昨天还正常,今天没改配置就停了,先怀疑移动了默认值的软件包或固件升级,再怀疑网络。
不要只看第一个错误,要读数据通道建立前后的日志。协商出的压缩模式、dev 类型和被推送的选项都会在更高日志级别出现,它们会告诉你属于三种不一致中的哪一种。
压缩是帧格式问题,不是带宽问题
启用压缩后,每个数据包都带有一个描述其编码方式的头部,两端必须就该头部达成一致。仍然带着旧示例配置中 comp-lzo 的服务器,配上没有压缩行的客户端,就会产生这个错误:服务器期望的头部,客户端从未写过。把 comp-lzo 与 compress lz4-v2 混用同样会失败,因为算法不同,尽管两者都叫压缩。在现代链路上,这个选项是帧格式契约,而不是带宽功能。
版本行为又添了一个陷阱。2.5 系列开始对压缩选项发出弃用警告,2.6 则划清了拒绝压缩包与仅仅让数据不压缩之间的界线。因此,路由器固件升级可能在没人修改隧道的情况下改变实际约定。
两端对齐,或者两端都删掉
列出所有压缩指令:服务器文件、任何推送的选项、针对特定客户端的文件以及客户端配置文件。服务器上的 comp-lzo 需要客户端上也有同样的词,而 adaptive 是旧默认值,在较旧的构建中裸选项通常就指它。路由器 GUI 上标着 LZO 或压缩的开关写入的也是同一个选项,所以要设成一致,而不是混用算法。不要以为名字相似就兼容。
如果不需要压缩,就有意从两端删除。服务器上的 allow-compression no 会拒绝压缩包,但只有在客户端停止提供压缩之后才有效;在客户端仍推送压缩时启用它,会在相反方向重新造成不一致。
检查推送和设备类型
服务器可以从主文件或针对特定客户端的文件推送压缩指令,客户端可以用 pull-filter 丢弃被推送的选项。以日志级别 4 连接一个客户端,对照服务器公布的内容和客户端接受的内容,然后确认没有过滤器把它删掉。如果两端现在一致而错误仍在,查看设备类型:一端的 dev tap 和另一端的 dev tun 无法交换帧,内核同样会拒绝写入。
桥接 TAP 部署需要服务器上有一个真正的网桥,而不只是换个设备名。把接口改名为 tap0 却不加入网桥,得到的设备会接受写入,却哪里也去不了。
然后用一个客户端和一个目的地验证:先 ping 服务器的隧道地址,再 ping 它局域网中的一台主机。前者证明数据通道,后者证明转发。