How to build a home soft smart router

home router

为什么要瞎折腾

今年年初买了个机柜放到家里,部署好了 google 的 Go builder 之后,一直忙于工作,都没有时间再折腾,作为一个 “计算机科学家”,平时工作需要经常从网上搬运代码(c+v && c+v), 众所周知,google 搜索的质量比国内的搜索引擎好太多,关键字提取的好第一页就能找到自己想要的内容,但是频繁的进行微皮恩开关还是稍显麻烦,而且还要配置家里的每一个终端,能不能让家里的网络很轻松的访问到某些学习资源呢?

好的程序员一定是懒的,一定是不能吃苦的。

思路

首先需要一个能配置(有 Linux kernel)的路由器,正好朋友最近在考虑换家里的路由器,于是一起研究了下,发现基于 OpenWRT 的设备可以满足我们的需求,于是在网上火速下单了下面这款设备,gl.inet mt1300:

home router

CPU: MT7621A @880MHz RAM: DDR3L 256MB WIFI: 2.4GHz (400Mbps), 5GHz (867Mbps)

从界面高级选项中打开安装 luci,通过 ssh 登录路由器内,安装 wireguard 通过国际 CN2 线路打通到硅谷的隧道,这里自己琢磨,不敢详述。

我们添加下面两条路由来平滑的修改默认路由 (无需删除之前的默认路由,也不会引起网络中断,感谢R):

Destination     Gateway         Genmask         Flags Metric Ref    Use Iface
0.0.0.0         10.0.1.1        128.0.0.0       UG    0      0        0 wg0
128.0.0.0       10.0.1.1        128.0.0.0       UG    0      0        0 wg0

这样所有的流量都直接从硅谷出去了,但是国内的资源会非常慢,怎么办呢,可以通过路由进行分流,OpenWRT 就是一个开源的 Linux,想怎么写路由都可以,首先我们从 apnic 上下载会最新的 IP 地址文件,把国内的 IP 地址段全部拿出来并转换成路由需要的 CIDR 格式:

curl 'http://ftp.apnic.net/apnic/stats/apnic/delegated-apnic-latest' | grep ipv4 | grep CN | awk -F\| '{ printf("%s/%d\n", $4, 32-log($5)/log(2)) }' > chnroute.txt

一共 8618 条地址块,有点多,我们写个 go 程序看看能不能再合并一下:

package main

import (
	"fmt"
	"io/ioutil"
	"strings"

	"github.com/EvilSuperstars/go-cidrman"
)

func main() {
	raw, err := ioutil.ReadFile("./chnroute.txt")
	if err != nil {
		panic(err)
	}

	lines := strings.Split(string(raw), "\n")
	var cidrs []string
	for _, l := range lines {
		if l == "" {
			continue
		}
		cidrs = append(cidrs, l)
	}

	newCIDRs, err := cidrman.MergeCIDRs(cidrs)
	if err != nil {
		panic(err)
	}

	for _, c := range newCIDRs {
		fmt.Printf("%s\n", c)
	}
}

合并完为 5434 条,少了 3000 多条,但是感觉还是有点多,不过确实合并不了了,大量的 /22、/23 的地址块无法向上合并了。

到底行不行

直接写个 shell 将 5000 多条路由以静态路由的方式写入配置文件,这样下次重启无需重新配置,OK,重启路由测试。发现路由信号直接消失了,第一反应是完了,需要重置路由器了,之前的 wg 需要重新配置了。过了好久信号回来了,但是无法获取到 DHCP 下发的地址,怀疑是静态路由太多导致 DHCP Server 起不来了,死马当成活马医,直接手写一个 IP,居然 ping 通了网关,进去清理掉了路由,看来性能确实不行啊,那只能是家里存在两个 wifi,一个用于国内资源访问,一个用于访问海外资源,常用办公的电脑链接海外 wifi,台式娱乐机链接到国内的路由器上,看来也能用。

有一天和朋友聊起这个问题,国内的 CIDR 地址块能不能再减少一些,其实如果不是那么精确,合并成一些大一点的 CIDR 其实可能问题也不大,但是要自己写一个合并策略和条件,想着有时间可以搞一下。还和朋友讨论了是不是可以搞一台专业点的路由器提升下性能,比如淘汰下来的 思科 1900 系列,home lab 的组建强调的是节能和静音,你们觉得下面这个东西可能吗?

home router

在咸鱼上翻了好久的路由器,回去又盯着机柜发呆,突然在机柜的角落里看到一层灰的树莓派,emmm,它的性能很强啊,作为 Arm 架构的硬件,也很节能。要不试一试。说干就干,直接在树莓派官网下载系统安装工具,挑一个喜欢的系统安装,我选择的是 Ubuntu,接着安装 wg,导入那让人头疼的 5000 多条路由,看起来没啥问题,也不卡。工作时的 status 如下:

Welcome to Ubuntu 21.10 (GNU/Linux 5.13.0-1008-raspi armv7l)

 * Documentation:  https://help.ubuntu.com
 * Management:     https://landscape.canonical.com
 * Support:        https://ubuntu.com/advantage

  System information as of Tue Dec 28 13:57:25 UTC 2021

  System load:           0.08
  Usage of /:            8.8% of 28.95GB
  Memory usage:          27%
  Swap usage:            0%
  Temperature:           35.9 C
  Processes:             144
  Users logged in:       0
  IPv4 address for eth0: 10.0.0.10
  IPv6 address for eth0: fd6b:bbbf:837b::c1e
  IPv6 address for eth0: fd6b:bbbf:837b:0:ba27:ebff:fec0:d41
  IPv4 address for wg0:  10.0.1.3


37 updates can be applied immediately.
To see these additional updates run: apt list --upgradable

如果我们使用一个 Linux 来做软路由,记得要把内核的转发打开,在 /etc/sysctl.conf 中添加:

net.ipv4.conf.default.forwarding=1  
net.ipv4.conf.all.forwarding=1

记得 sysctl -p 生效,当然还有 NAT with iptables 功能也要开启,这个要使用 iptables 来支持。

iptables -t nat -A POSTROUTING -j MASQUERADE

OK, 一台支持智能分流的树莓派软路由完成配置了,但是目前我们在树莓派上配置的 DNS 是阿里云的多线 DNS: 223.5.5.5,由于众所周知的问题,国内 DNS 多多少少有点问题(如果你不知道就很难看懂),那么能不能直接用国外的 DNS 呢,比如 Google 的 8...8 ?答案是不能,因为很多内容服务商都有基于 DNS 的智能解析,我可不想在国内访问斗鱼时给我调度到国外的 CDN 上去,那么看来 DNS 解析也需要分流了。

总有前人走过的路

在树莓派上安装 dnsmasq 就可以完成 DNS 解析的缓存、分流、拦截等操作,接着树莓派的 resolve 文件调整为:

nameserver 127.0.0.1
nameserver 223.5.5.5

我们默认走本地,然后是国内的 DNS,但是哪些域名需要走国外的 DNS 呢?github 是个好地方,不能写太多,给点提示,聪明的你肯定能找到:

wget https://github.com/cokebar/***2dnsmasq/blob/master/***2dnsmasq.sh
bash ./***2dnsmasq.sh -d 8.*.*.8 -p 53 -o ./dns.conf

home router

最后 reload dnsmasq,OK,你现在可以在一个网络中既能快速的偷学海外学习资料,又能稳定的沉浸在斗鱼的直播中了。对了,有人收我那台 OpenWRT 的路由器吗,兄弟价。