家用智能路由器
路由器技术选型
我将目前主要的方案分成三类:
-
软路由
使用高性能的台式机或者服务器配合软件实现路由器的所有功能。同等硬件条件下,软路由往往不如硬路由。但是 PC 价格真的太低了。 尤其是目前某宝有许多专门为软路由设计的硬件。同等价格下的家用路由器,软路由非常不错。知乎上一位大佬对于软路由的评价非常真切:
其实很多家用路由器都是工作在软路由模式下。虽然会有 NAT 转发芯片,但是只要你在路由器里启用了家长控制、流量统计、QOS 限速、广告过滤、不可描述之类的功能,这个芯片就歇菜了。包处理的担子还是要交给 CPU。然后就是 MIPS/ARM 的性能被 X86 吊打。
另外,软路由安装和使用更简单。交互也更加友好。非常适合新人入门使用。
-
硬路由
这只是相对软路由的称呼,也就是指的传统的路由器。不过直接从厂商买来的路由器功能并不太强大。需要用于手动刷固件从而获得更强大的功能。
-
智能路由
直接购买将“智能”作为噱头的路由器。这类路由器无法提供一些不可描述的功能。
我家中选择的是硬路由的方案。一是因为家中有多台网络设备,高端的硬路由能提供更大的宽带和更好的性能。二是我自信能解决遇到的复杂技术问题。
设配选购/技术选型
-
路由器:WRT3200ACM
WRT 系列路由器与OpenWRT大有渊源。可以轻易刷成 OpenWRT 系统,并且不需要担心路由器变砖的问题。 WRT3200ACM 更是 WRT 系列中的高配版本,开启一系列插件后依旧能否轻松负载千兆宽带。
-
路由固件:OpenWRT
OpenWRT其实是一个轻量级的 Linux。潘多拉和受软路由钟爱的LEDE固件均源于OpenWRT。 另外两个流行的固件:Padavan(又称老毛子)和Merlin(又称梅林)都源于华硕的官方固件。 之所以选择OpenWRT,最主要的原因是其官方网站具有最完善的官方说明文档; 其次官方版本的OpenWRT也是最轻量的,可以根据自己的需要进行定制。
-
NAS: Synology DS720+
虽然OpenWRT可以通过安装
samba
插件为局域网提供文件共享服务,但是在路由器上安装的扩展磁盘无处安放且散热不畅。 更何况,独立的 NAS 能够提供更友好的操作界面、磁盘备份、图片/视频管理软件、邮件服务等。NAS 本身也非常适合安装的软路由的,不过我并不打算这样做。如果资金不足又想要得到所有功能,购买一个 NAS 搭配软路由也是不错的选择。
知识储备
- 了解 Linux 系统的基本原理,并能熟练的编写 Shell 脚本。
- 有一定的编码基础和较强的动手能力。我们需要动手编译一些OpenWRT官方未提供的不可描述的插件。
- 能顺畅使用Docker。我们需要搭建一个可重复使用的编译环境。
上传固件
- 首先需要进入OpenWRT 官网下载最新版本的 OpenWRT 固件。需要根据购买的路由器型号进行选择。
- WRT 系列路由器可以直接上传OpenWRT固件,上传完成后重启路由器即可。其他路由器请自行查找刷固件的技术方案。
OpenWRT 基础配置
OpenWRT虽然也提供配置页面。但既然是一个 Linux 系统,便可以通过ssh
登录到命令行进行配置。
虽然配置页面听起来虽然不错。但是在配置完成前,页面交互体验还是非常不友好的。
并且使用shell
命令进行配置,我们便可将之整理成脚本以备重置路由器时使用。
包管理工具
每个 Linux 系统都有自己的包管理工具。OpenWRT使用opkg
命令作为包管理工具。使用方法详见说明文档。
我们第一步便是更新可用软件包列表:
opkg update
# 升级所有的软件包
opkg list-upgradable | cut -f 1 -d ' ' | xargs opkg upgrade
为了以后使用方便,我们设定定时任务每日更新软件包列表
touch /etc/root/root
echo "0 4 * * * opkg update" > /etc/crontabs/root
/etc/init.d/cron enable
支持 HTTPS
OpenWRT 并没有默认安装openssl
,导致无法在命令行发送https
请求。由于我们之后配置需要的一些文件必须通过https
下载,因此先安装这个插件:
opkg install libustream-openssl
页面美化
luci-theme-argon是一个使用较为广泛且比较好看的页面样式插件。我非常喜欢 Material Design,便选择了此插件。
# 汉化
opkg install luci-i18n-base-zh-cn luci-i18n-opkg-zh-cn luci-i18n-firewall-zh-cn
# 安装 luci-theme-argon
wget --no-check-certificate https://github.com/jerrykuku/luci-theme-argon/releases/download/v2.2.5/luci-theme-argon_2.2.5-20200914_all.ipk -O luci-theme-argon.ipk
opkg install luci-theme-argon.ipk
安装完成后,重新访问配置页面即可。
如果发现页面依旧存在部分英文,是由于浏览器缓存导致。删除浏览器缓存后再次刷新页面即可。
路由器实时监控
luci-app-statistics
:实时监控插件luci-i18n-statistics-zh-cn
:实时监控的汉化插件collectd-mod-curl
:扩展实时监控插件功能:使用curl
指令实时监控网络连通性和响应时间。
opkg install luci-app-statistics luci-i18n-statistics-zh-cn collectd-mod-curl
# 启用 collectd-mod-curl
uci set luci_statistics.collectd_curl.enable='1'
# 监控baidu.com的响应时间
uci add luci_statistics collectd_curl_page
uci set luci_statistics.@collectd_curl_page[-1].enable='1'
uci set luci_statistics.@collectd_curl_page[-1].name='BaiDu'
uci set luci_statistics.@collectd_curl_page[-1].url='baidu.com'
# 监控google.com的响应时间
uci add luci_statistics collectd_curl_page
uci set luci_statistics.@collectd_curl_page[-1].enable='1'
uci set luci_statistics.cfg288c80.name='Google'
uci set luci_statistics.cfg288c80.url='google.com'
uci commit
uci
是“Unified Configuration Interface”(统一配置界面)的缩写,意在 OpenWrt 整个系统的配置集中化。
uci
指令用于管理 OpenWRT 的配置。使用指令比直接修改配置文件方便太多了,使用方法详见说明文档
无线网络配置
此配置建议进入配置页面进行配置。如果打算整理初始化脚本,以下代码可做参考:
PASSWORD="changeme"
ESSID="无线账户"
uci set wireless.radio0.channel='auto'
uci set wireless.radio0.htmode='VHT160'
uci set wireless.default_radio0.key=$PASSWORD
uci set wireless.default_radio0.ssid=$ESSID
uci set wireless.default_radio0.encryption='psk2'
uci set wireless.default_radio1.key=$PASSWORD
uci set wireless.default_radio1.ssid=$ESSID
uci set wireless.default_radio1.encryption='psk2'
uci set wireless.radio2.channel='auto'
uci set wireless.default_radio2.key=$PASSWORD
uci set wireless.default_radio2.ssid=$ESSID
uci set wireless.default_radio2.encryption='psk2'
uci commit
WRT3200ACM 有三个频段(radio0
、radio1
、radio2
)。这与路由器硬件息息相关,需要根据路由器型号编写脚本。
wireless.radio0.htmode='VHT160'
也是 WRT3200ACM 独有的带宽,也因此导致了 WRT3200ACM 无法在国内上市。
编译 OpenWRT 软件包
很多不可描述的插件并不能直接从 OpenWRT 官方软件源安装,需要自己动手编译。 不过由于路由器性能比较低,并不适合直接在路由器上编译 ipk 包。我们需要通过 OpenWRT SDK 交叉编译软件包。
构建编译环境
相比安装一个 Linux 系统,使用 Docker 来构建编译环境会方便许多。 首先,需要根据路由器型号下载对应的 OpenWRT SDK。 这里我们使用 WRT3200ACM 需要的 SDK:
FROM ubuntu
WORKDIR /root
ENV DEBIAN_FRONTEND noninteractive
ENV OPENWRT_SDK openwrt-sdk-19.07.4-mvebu-cortexa9_gcc-7.5.0_musl_eabi.Linux-x86_64
COPY ${OPENWRT_SDK}.tar.xz /root/${OPENWRT_SDK}.tar.xz
RUN apt-get update && \
apt-get install xz-utils vim -y && \
tar -xvf /root/${OPENWRT_SDK}.tar.xz && \
mv /root/${OPENWRT_SDK} /root/sdk && cd /root/sdk &&\
apt-get install build-essential ccache flex gawk gettext git liblzma-dev libncurses5-dev libssl-dev python subversion u-boot-tools unzip wget xsltproc zlib1g-dev python3 rsync -y && \
./scripts/feeds update -a && \
./scripts/feeds install -a
更多细节参考GitHub
构建完成后,为之后方便使用可以发布至 DockerHub。至此我们便准备好了编译环境。
编译软件包
我们需要的软件包:
- openwrt-shadowsocksr: ssr 插件
- luci-app-shadowsocksr: ssr 插件管理界面
- openwrt-ckipver: ssr 订阅功能
- openwrt-dns-forwarder: ssr 的 DNS 防污染功能
- openwrt-cdns: ssr 的 DNS 防污染列表扩展之一
- openwrt-pdnsd: ssr 的 DNS 防污染列表扩展之一
- ddns-scripts_aliyun: 阿里云 DDNS 服务
使用我们刚刚构建的编译环境:
FROM valistarguo/openwrt-sdk-arm_cortex-a9_vfpv3-d16 as builder
WORKDIR /root/sdk
COPY .config /root/sdk/.config
RUN cd /root/sdk && \
git clone https://github.com/Hill-98/luci-app-shadowsocksr.git package/luci-app-shadowsocksr && \
git clone https://github.com/Hill-98/openwrt-ckipver.git package/ckipver && \
git clone https://github.com/Hill-98/openwrt-cdns package/cdns && \
git clone https://github.com/Hill-98/openwrt-shadowsocksr package/shadowsocksr-libev && \
git clone https://github.com/sensec/ddns-scripts_aliyun.git package/ddns-scripts_aliyun && \
git clone https://github.com/aa65535/openwrt-dns-forwarder.git package/dns-forwarder && \
git clone https://github.com/Hill-98/openwrt-pdnsd package/pdnsd && \
cd package/luci-app-shadowsocksr/tools/po2lmo && \
make && make install && \
cd /root/sdk
RUN make package/shadowsocksr-libev/compile package/ddns-scripts_aliyun/compile package/luci-app-shadowsocksr/compile package/ckipver/compile package/cdns/compile package/dns-forwarder/compile package/pdnsd/compile -j$((`nproc`+1)) V=s && \
make package/index V=s
更多细节参考GitHub
搭建软件源
虽然直接在 Docker 中构建好 ipk 软件包,然后直接上传到OpenWRT安装没什么问题。但是这并不方便长久的管理软件包。 搭建独立的软件源,非常方便后续重置路由器时再次安装需要的软件。接下来我们需要做的事情:
- 生成软件源的公钥和私钥,OpenWRT 添加新的软件源需要对软件源进行验证。
- 添加 Nginx 服务,提供软件包和公钥下载服务
我们从上一节dockerfile
的基础上继续改动:
FROM valistarguo/openwrt-sdk-arm_cortex-a9_vfpv3-d16 as builder
WORKDIR /root/sdk
COPY .config /root/sdk/.config
RUN cd /root/sdk && \
git clone https://github.com/Hill-98/luci-app-shadowsocksr.git package/luci-app-shadowsocksr && \
git clone https://github.com/Hill-98/openwrt-ckipver.git package/ckipver && \
git clone https://github.com/Hill-98/openwrt-cdns package/cdns && \
git clone https://github.com/Hill-98/openwrt-shadowsocksr package/shadowsocksr-libev && \
git clone https://github.com/sensec/ddns-scripts_aliyun.git package/ddns-scripts_aliyun && \
git clone https://github.com/aa65535/openwrt-dns-forwarder.git package/dns-forwarder && \
git clone https://github.com/Hill-98/openwrt-pdnsd package/pdnsd && \
cd package/luci-app-shadowsocksr/tools/po2lmo && \
make && make install && \
cd /root/sdk
RUN make package/shadowsocksr-libev/compile package/ddns-scripts_aliyun/compile package/luci-app-shadowsocksr/compile package/ckipver/compile package/cdns/compile package/dns-forwarder/compile package/pdnsd/compile -j$((`nproc`+1)) V=s && \
make package/index V=s && \
./staging_dir/host/bin/usign -G -s mime.key -p mime.pub && \
./staging_dir/host/bin/usign -S -m bin/packages/arm_cortex-a9_vfpv3-d16/base/Packages -s mime.key -x bin/packages/arm_cortex-a9_vfpv3-d16/base/Packages.sig && \
mv mime.pub bin/packages/arm_cortex-a9_vfpv3-d16/base/mime.pub
FROM nginx
RUN mkdir /usr/share/nginx/packages
COPY nginx.conf /etc/nginx/nginx.conf
COPY --from=builder /root/sdk/bin/packages/arm_cortex-a9_vfpv3-d16/base/ /usr/share/nginx/packages
RUN chown -R nginx /usr/share/nginx/packages && chgrp -R nginx /usr/share/nginx/packages
更多细节参考GitHub
接下来我们只需要将编译完成的 docker 镜像部署在一台服务器上。也可以部署在家中的 NAS 上,访问速度更快也更安全。
OpenWRT 高级配置
添加软件源
添加我们刚部署好的软件源:
wget http://openwrt.val-istar-guo.com/mime.pub
opkg-key add mime.pub
echo "src/gz vg http://openwrt.val-istar-guo.com/" > /etc/opkg/customfeeds.conf
opkg update
动态 DNS
由于我们没有固定 IP,即使联系网络供应商也只能获得一个动态 IP。动态 DNS(DDNS)便可以帮助我们自动的修改域名的主机记录值。 这里需要根据自己的域名供应商进行选择和配置:
opkg install ddns-scripts luci-app-ddns luci-i18n-ddns-zh-cn ddns-scripts_aliyun
DDNS_USERNAME="username"
DDNS_PASSWORD="password"
DDNS_HOST="myhost.example.com"
DDNS_DOMAIN="myhost@example.com"
uci set ddns.ddns_ipv4=service
uci set ddns.ddns_ipv4.service_name='aliyun.com'
uci set ddns.ddns_ipv4.lookup_host=$DDNS_HOST
uci set ddns.ddns_ipv4.domain=$DDNS_DOMAIN
uci set ddns.ddns_ipv4.username=$DDNS_USERNAME
uci set ddns.ddns_ipv4.password=$DDNS_PASSWORD
uci set ddns.ddns_ipv4.enabled='1'
uci commit
这里需要注意,如果doamin
是二级域名,需要使用@
代替.
作为分隔符(也不知道是哪个傻逼这么设计的,搞得跟邮箱似的)。
不可描述的上网方式
我们要使用刚才编译好的软件源安装相关插件:
# 必要依赖
opkg install libpcre libev libsodium libudns
opkg install ipset
# 支持UDP协议透明代理
opkg install iptables-mod-tproxy ip
# DNS污染列表解析
opkg remove dnsmasq
opkg install dnsmasq-full
# 获取 DNS 域名污染列表和服务器订阅数据
opkg install curl
# base64 解码 DNS 域名污染列表和服务器订阅数据
opkg install coreutils-base64
# 服务器订阅脚本使用 bash 解释器运行
opkg install bash
# 用于订阅脚本解析域名
opkg install bind-dig
# 后续安装需要私搭的软件源
# 用于订阅脚本检测 IP 地址合法性
opkg install ckipver
# 防污染包
opkg install cdns dns-forwarder pdnsd
opkg install shadowsocksr-libev luci-app-shadowsocksr
安装完成后,我们可以进入路由器的配置页面添加相关配置。配置完成后需要重启路由器才能生效。
首次安装后需要重启路由器,可能是由于
shadowsocksr-libev
没有在安装时启动导致。
NAS
Synology DS720+并不需要在路由器中添加太多配置,我们需要做的只是为Synology DS720+的两个网卡其提供固定的局域网 IP:
uci add dhcp host
uci set dhcp.@host[-1].mac=$NAS_MAC_1
uci set dhcp.@host[-1].dns='1'
uci set dhcp.@host[-1].name='NAS'
uci set dhcp.@host[-1].ip='192.168.1.2'
uci set dhcp.@host[-1].mac=$NAS_MAC_2
uci set dhcp.@host[-1].dns='1'
uci set dhcp.@host[-1].name='NAS'
uci set dhcp.@host[-1].ip='192.168.1.3'
uci commit
Synology DS720+ 提供了许多不错的软件:Photo Station
、Vidio Station
、MAIL邮箱服务
。
为了能够在外网访问这些服务,我们需要修改防火墙配置:
# NAS管理界面
uci add firewall redirect
uci set firewall.@redirect[-1].dest_port='5000'
uci set firewall.@redirect[-1].src='wan'
uci set firewall.@redirect[-1].name='NAS'
uci set firewall.@redirect[-1].src_dport='5000'
uci set firewall.@redirect[-1].target='DNAT'
uci set firewall.@redirect[-1].dest_ip='192.168.1.2'
uci set firewall.@redirect[-1].dest='lan'
# Photo Station服务
uci add firewall redirect
uci set firewall.@redirect[-1].dest_port='80'
uci set firewall.@redirect[-1].src='wan'
uci set firewall.@redirect[-1].name='Photo Station'
uci set firewall.@redirect[-1].src_dport='19000'
uci set firewall.@redirect[-1].target='DNAT'
uci set firewall.@redirect[-1].dest_ip='192.168.1.2'
uci set firewall.@redirect[-1].dest='lan'
# Vidio Station服务
uci add firewall redirect
uci set firewall.@redirect[-1].dest_port='9007'
uci set firewall.@redirect[-1].src='wan'
uci set firewall.@redirect[-1].name='Vidio Station'
uci set firewall.@redirect[-1].src_dport='18000'
uci set firewall.@redirect[-1].target='DNAT'
uci set firewall.@redirect[-1].dest_ip='192.168.1.2'
uci set firewall.@redirect[-1].dest='lan'
# MAIL邮箱服务
uci add firewall redirect
uci set firewall.@redirect[-1].dest_port='25'
uci set firewall.@redirect[-1].src='wan'
uci set firewall.@redirect[-1].name='SMTP'
uci set firewall.@redirect[-1].src_dport='25'
uci set firewall.@redirect[-1].target='DNAT'
uci set firewall.@redirect[-1].dest_ip='192.168.1.2'
uci set firewall.@redirect[-1].dest='lan'
uci add_list firewall.@redirect[-1].proto='tcp'
uci add firewall redirect
uci set firewall.@redirect[-1].dest_port='143'
uci set firewall.@redirect[-1].src='wan'
uci set firewall.@redirect[-1].name='IMAP'
uci set firewall.@redirect[-1].src_dport='143'
uci set firewall.@redirect[-1].target='DNAT'
uci set firewall.@redirect[-1].dest_ip='192.168.1.2'
uci set firewall.@redirect[-1].dest='lan'
uci add firewall redirect
uci set firewall.@redirect[-1].dest_port='110'
uci set firewall.@redirect[-1].src='wan'
uci set firewall.@redirect[-1].name='POP3'
uci set firewall.@redirect[-1].src_dport='110'
uci set firewall.@redirect[-1].target='DNAT'
uci set firewall.@redirect[-1].dest_ip='192.168.1.2'
uci set firewall.@redirect[-1].dest='lan'
uci add_list firewall.@redirect[-1].proto='tcp'
uci add firewall redirect
uci set firewall.@redirect[-1].dest_port='465'
uci set firewall.@redirect[-1].src='wan'
uci set firewall.@redirect[-1].name='SMTP-SS'
uci set firewall.@redirect[-1].src_dport='465'
uci set firewall.@redirect[-1].target='DNAT'
uci set firewall.@redirect[-1].dest_ip='192.168.1.2'
uci set firewall.@redirect[-1].dest='lan'
uci add_list firewall.@redirect[-1].proto='tcp'
uci add firewall redirect
uci set firewall.@redirect[-1].dest_port='587'
uci set firewall.@redirect[-1].src='wan'
uci set firewall.@redirect[-1].name='SMTP-TLS'
uci set firewall.@redirect[-1].src_dport='587'
uci set firewall.@redirect[-1].target='DNAT'
uci set firewall.@redirect[-1].dest_ip='192.168.1.2'
uci set firewall.@redirect[-1].dest='lan'
uci add_list firewall.@redirect[-1].proto='tcp'
uci add firewall redirect
uci set firewall.@redirect[-1].dest_port='993'
uci set firewall.@redirect[-1].src='wan'
uci set firewall.@redirect[-1].name='IMAP-SSL/TLS'
uci set firewall.@redirect[-1].src_dport='993'
uci set firewall.@redirect[-1].target='DNAT'
uci set firewall.@redirect[-1].dest_ip='192.168.1.2'
uci set firewall.@redirect[-1].dest='lan'
uci add_list firewall.@redirect[-1].proto='tcp'
uci add firewall redirect
uci set firewall.@redirect[-1].dest_port='995'
uci set firewall.@redirect[-1].src='wan'
uci set firewall.@redirect[-1].name='POP3-SSL/TLS'
uci set firewall.@redirect[-1].src_dport='995'
uci set firewall.@redirect[-1].target='DNAT'
uci set firewall.@redirect[-1].dest_ip='192.168.1.2'
uci set firewall.@redirect[-1].dest='lan'
uci add_list firewall.@redirect[-1].proto='tcp'
uci commit firewall
OpenWRT 初始化脚本
上述所有的脚本已经整理至GitHub。 至此,我所需要的路由器功能都已添加。本文若对你有帮助,还请在 GitHub 点个 Star。