本文发自 http://www.binss.me/blog/run-side-router-on-synology-nas/,转载请注明出处。

背景

对于科学上网,本人习惯于在设备本身上安装科技软件。相比在路由器上安装,一来设备(如手机、电脑)的处理能力强大,科技本身并不会有性能瓶颈,二来移动设备在切换网络后依然可以保持科学。但并非所有设备都能够安装科技软件,比如 AppleTV (商店不提供)、Oculus Quest(装了 Android 的科技软件后经常不明抽风)、PS5(不支持)等,为此还是需要一种在设备外对流量进行代理的方案,比如路由器科学上网。说到路由器科学上网,脑海中直接飘过几年前刷潘多拉、梅林、Openwrt 的恐惧,当年热衷于折腾路由器的我最终因为网络不稳定的原因换回了原生固件的路由器,之前是 AC86U ,后来为了支持 Wifi6 换了 ax6 。

得到的教训是,路由器作为网络的唯一出口: keep it simple and stable 是首要原则,因此考虑旁路由方案。即让需要辅助科学上网的设备的流量到达主路由后被转发到到旁路由上进行处理,最后再从主路由出去,其他设备还是保持直接走主路由。

经过取舍,我放弃了 iMac 现有 Surge for mac 做代理的方案,而是选择 Nas ,一台群晖 DS918+ ,一来常年开机且接了 UPS,二来性能上够用。那么怎么让群晖作为软路由呢?有 docker 和 vm 两种方案,我都折腾了一遍,最后选择了 vm 。

Docker 方案

起一个 openwrt 容器,装 openclash 。

  1. 网卡开启混杂模式

    sudo ip link set eth0 promisc on
  2. 下载 openwrt image,比如 openwrtorg/rootfs ,经过测试 x86_64-21.02-SNAPSHOT 是比较合适的

  3. 创建一个 macvlan

    docker network create -d macvlan --subnet=192.168.10.0/24 --gateway=192.168.10.1 -o parent=eth0 vrouter

    其中 192.168.10.0/24 为你当前局域网的网段,192.168.10.1 为当前网关(主路由 ip),eth0 为群晖网卡设备名(分配有局域网 ip 的那个),vrouter 为要创建的子网名称,可以随便取

  4. 启动一个特权容器,连接到 vrouter 子网上,删除掉所有的端口映射,并通过 docker exec -it openwrt sh 拿到 shell (openwrt 自带没有 bash)

  5. 修改 /etc/config/network 配置,手动指定 ip 和 dns ,如

    config interface 'lan'
        option type 'bridge'
        option ifname 'eth0'
        option proto 'static'
        option idaddr '192.168.10.2'
        option netmask '255.255.255.0'
        option dns '192.168.10.1'

    然后 /etc/init.d/network restart 加载配置。

  6. 尝试 ping baidu,确认能连接外网后,开始安装科学上网软件,这里选择 openclash

    opkg update
    opkg install luci luci-base iptables dnsmasq-full coreutils coreutils-nohup bash curl jsonfilter ca-certificates ipset ip-full iptables-mod-tproxy kmod-tun luci-compat

    会发现安装 dnsmasq-full 失败,需要先卸载掉 dnsmasq 再安装

    opkg remove dnsmasq
    opkg install dnsmasq-full

    openclash 还依赖于 libcap 相关库,需要安装:

    opkg install libcap libcap-bin

    如果提示找不到 libcap ,则需要手动添加源,在 /etc/opkg/distfeeds.conf 添加一行

    src/gz snapshots http://downloads.openwrt.org/snapshots/packages/x86_64/base

    然后 opkg update 。

    最后安装 openclash 的包,需要从 github 上下载,比如 https://github.com/vernesong/OpenClash/releases/download/v0.42.05-beta/luci-app-openclash_0.42.05-beta_all.ipk

  7. 安装完后,在 openwrt 的 luci 界面会多了一栏 services ,里面有 openclash ,点击打开界面,导入配置后启动 openclash 即可

相比 vm 方案,该方案安装简单,但坑的地方在于,试过了各种模式都不支持 udp 转发,在某些模式上甚至连不了网。推测是这些功能依赖于 kmod ,虽然在容器中安装了,但实际上用的还是 host kernel ,而 host 并未安装。一种可行的方法是给群晖的 kernel 编个 kmod ,但需要安装群晖一堆的构建套件,以前为了编 Wireguard 试过一次,非常痛苦,建议不要折腾。

VM 方案

注:群晖的 VM 支持要求 CPU 支持虚拟化 ,比如我是 DS918+ ,CPU 是 J3455 ,支持虚拟化。

vm 方案的缺点在于,guest 存在虚拟化开销,并且由于群晖对虚拟机的支持非常有限(没 vt-d),网卡只能通过 virtio 或者模拟设备的方式给 vm 用。但鉴于 docker 方案不支持 UDP,只能将就用了。类似于 docker 方案,起一个 openwrt vm,装 openclash 。

  1. 下载 openwrt image ,我用的是 https://downloads.openwrt.org/releases/19.07.7/targets/x86/64/combined-squashfs.img.gz
  2. 群晖在 Virtual Machine Manage - 映像 - 硬盘映像 导入该镜像,并在 虚拟机 - 导入 - 从硬盘映像导入 ,给个 1C1G ,硬盘选择刚刚导入的镜像,网络选择默认的即可
  3. 启动虚拟机,通过 虚拟机 - 连接拿到 shell ,同样修改 /etc/config/network 配置,手动指定 ip 和 dns 并重新加载配置。这时候 ping 测试会发现虽然能解析到 ip 但连接不上。这是因为缺少一条路由指令,建议加入到 /etc/config/network 持久化:

    config route 'external'
        option interface 'lan'
        option target '0.0.0.0'
        option netmask '0.0.0.0'
        option gateway '192.168.10.1'

    重新加载配置即可

  4. 同 docker 方案,安装 openclash

接入

在需要科学上网的设备上,手动指定 ip ,并将网关和 dns 指定为旁路由的 ip ,我这里为 192.168.10.2 ,即可让旁路由接管流量。

个人比较依赖的是让 udp 包能够科学,因为通常游戏的联机都是通过 UDP 来实现的,为此可以通过 PS5 的网络连接测试看看网络类型,如果能够联通说明 udp 科学上网成功。除了要求 Openclash 开启 udp 转发外,还要求你科学的节点是要支持 udp 的,可通过查看配置里是否有 udp: true 来判断。