Skip to content

为什么做 Landscape

项目起源于一次 OpenWrt 崩溃。

崩溃本身并非 OpenWrt 的问题。如果只是使用原版 OpenWrt,那么确实挺稳定的;问题出在我用的几个第三方插件之间。反复遇到这类兼容性问题之后,我开始认真考虑换个思路。

那时我恰好正在学习 Rust 和 eBPF,也想找一个自己真正会长期使用的项目来实践。于是有了最开始的想法:为什么不试着用它们做一个路由器?

长期使用中积累的问题

NAT 策略粒度不够。 在我当时使用的固件和插件组合中,NAT 行为基本是全局设置,很难针对不同设备和流量分开配置。BT/PT 可能需要 NAT1,其他程序却没这个必要,更不想让 PCDN 之类的程序白白占用上行带宽。

插件配置缺少清晰的边界。 我依赖的分流插件会统一修改系统中的 DNS、路由和防火墙状态。它们本身解决了很多问题,但不同设备的策略仍然共享同一套运行环境;插件之间一旦出现兼容问题或配置错误,被波及的往往远不止需要它们处理的那部分流量。

自定义固件更新比较繁琐。 OpenWrt 的 sysupgrade 可以保留标准配置,但我的自定义固件还包含额外的插件和文件。升级前需要逐一确认并备份,之后再重新刷入完整固件。自己编译固件耗时较长,构建环境或插件组合发生变化时,还可能在最后一步才发现失败。

内核版本和固件绑在一起。 内核及其模块需要与固件一起构建,不能像普通 Linux 发行版那样独立跟随较新的主线内核。新的网络能力和 eBPF 钩子,也只能等整套固件适配后再使用。

这些都不是说 OpenWrt 做得不好。它面向的是完整、可刷写的嵌入式路由系统,而我逐渐发现,自己更想要的是一套运行在普通 Linux 上、可以单独升级和组合的路由程序。

设计上的选择

最初选择 Rust 和 eBPF,主要是刚好在学,就拿来练手。Rust 用来编写需要长期运行的控制程序;在内核支持相应能力的前提下,eBPF 程序可以动态加载并挂载到 XDP 和 TC,在内核中完成数据包处理,不必把所有流量送入用户态,也不需要再围绕 iptables 组织一整套转发流程。

这也决定了 Landscape 的边界。Landscape 不是一个新的路由发行版,而是直接运行在 Debian、Arch、openSUSE 等标准 Linux 系统上。配置集中保存在一个目录中,升级时替换二进制即可,数据迁移由程序在启动时完成。

策略则由 Flow 隔离。设备通过 IP 或 MAC 加入不同 Flow,每个 Flow 分别管理 DNS、出口和 NAT。Landscape 默认采用比 NAT4 更严格的策略,再按端口、域名或 IP 为确实需要的流量放通 NAT1。需要代理的流量可以导入容器,其他流量继续走正常的 Linux 网络路径;即使容器发生故障,也不会拖着直连流量一起中断。

最开始并没有一张完整的设计图。只是这些问题已经困扰我很久,于是我决定做一个自己真正会使用的路由器。Landscape 就是从这个念头开始,随着实际使用一点点长出来的。