科学上不了网时的魔法诊断

/ 1评 / 0

「如何上网」逐渐成为一个非常复杂的问题。

由于一些众所周知的问题,这篇文章将不能透露太多关键信息。

无法与远端建立连接

在上回[1]抱怨过断连问题后,我物色了一个新的供应商。头两天还好好的,紧接着又不行了。

我寻思开会的时候执行力度是很大,但不至于这么大吧!手机看了一下官网情况,一切正常。

所以说,计算机没有科学,计算机全是魔法。该进行魔法诊断了。

网络拓扑

目前,我们的网络拓扑如下所示。注意:我们没有权利去操作光猫和光猫右侧的组件。

图 1 网络拓扑结构

路由器的 DHCP 服务器的配置为:

// in virtual file routerfs:/dhcp.conf

Router IP: 192.168.16.1
Subnet: 255.255.255.0

DHCP IP Range: 192.168.16.2 -- 192.168.16.254
Lease time: 86400
Default gateway: 192.168.16.1
DNS Server: <Obtain from ISP>
            192.168.16.1
            ^ [Overridden by 科学上网]

IP 与设备绑定的配置为:

// in virtual file routerfs:/ip-bind.conf

Device      IP Address
raspberrypi 192.168.16.110 # 树莓派
box         192.168.16.120 # 工控机
*           <Dynamic>

究竟断在哪里

从上一次指出「手机端尚可存续」的时候,我就隐隐地觉得这个还是 DNS 的问题。要验证这个问题很简单:

$ drill www.google.com

等了很长时间,DNS 查询超时。然而:

$ drill www.google.com @223.5.5.5

;; ->>HEADER<<- opcode: QUERY, rcode: NOERROR, id: 11804
;; flags: qr rd ra ; QUERY: 1, ANSWER: 1, AUTHORITY: 0, ADDITIONAL: 0 
;; QUESTION SECTION:
;; www.google.com.      IN      A

;; ANSWER SECTION:
www.google.com. 163     IN      A       31.13.95.38

;; AUTHORITY SECTION:

;; ADDITIONAL SECTION:

;; Query time: 7 msec
;; SERVER: 223.5.5.5
;; WHEN: Sat Mar 12 19:31:22 2022
;; MSG SIZE  rcvd: 48

好嘛,这一下子就知道是怎么回事了。虽然直接 DNS 查询返回的结果并不怎么正确,但至少是返回了结果。如果连结果都不返回,我只能说很刺激了。

迁移 DNS 服务

如果是这样的话,DNS 查询服务就不能靠路由器——至少不能靠路由器的魔法插件来做了。好在之前在学校的时候为了抗击学校内网严重的 DNS 污染,我有一个已经配置过 DNS 的树莓派可以用。在闲置了将近一年后,这个设备又要被返聘上岗了。

使用的方案很简单:dnsmasq. 如果要抗击 DNS 污染的话,需要额外配置 DoH. 但目前我们并不需要操心这个问题。所以基本上是即装即用,但是要配置两个内容:让 dnsmasq 询问所有的上游 DNS 服务器,以及我们希望使用的上游 DNS 服务器:

// in file raspberrypi!/etc/dnsmasq.conf

resolv-file=/etc/resolv.dnsmasq.conf
all-servers
// in file raspberrypi!/etc/resolv.dnsmasq.conf

nameserver 223.5.5.5
nameserver 223.6.6.6
nameserver 114.114.114.114
nameserver 1.1.1.1
nameserver 1.0.0.1

dnsmasq 服务启动之后,树莓派即可作为 DNS 服务器使用。我们还可以通过 dnsmasq-china-list 加速国内服务的 DNS 解析[2]。新建文件夹 /etc/dnsmasq.d. 下载 install.sh 并执行即可拉取必要的配置文件,之后配置 dnsmasq 使用这些配置文件:

// in file raspberrypi!/etc/dnsmasq.conf

...
conf-dir=/etc/dnsmasq.d

完成后重启 dnsmasq 服务以应用更改。

事实上无论如何我们都是需要操心 DNS 污染问题的。因为这玩意好像不太聪明的亚子。

首先,我们获取 cloudflared 作为 DoH 客户端[3]。我们将直接使用 Cloudflare 提供的 DoH 服务。当然如果你喜欢的话也可以添加阿里云的 DoH 服务[4],此处不再赘述。

编写一份服务文件让其作为后台服务启动:

// in file /etc/systemd/system/cloudflare.service

[Unit]
Description = Cloudflared DoH Client
Requires = network.target
After = network.target

[Service]
Type = simple
ExecStart = /opt/cloudflared proxy-dns --address 127.0.0.1 --port 12535

[Install]
WantedBy = multi-user.target

配置 dnsmasq, 使其不再使用任何 resolv.conf 文件,而使用我们指定的地址:

// in file raspberrypi!/etc/dnsmasq.conf

no-resolv
server=127.0.0.1#12535
all-servers

同时,删除从 dnsmasq-china-list 中拉取的关于 Google 的条目以避免相关条目被解析到国内地址(这会影响之后我们配置单臂路由的难度):

# rm /etc/dnsmasq.d/google*

最后(再次)重启 dnsmasq 服务以应用更改。

DNS 查询风暴

有趣的是,当 DNS 服务和路由器不在一起时,很容易构成 DNS 查询风暴:即两台设备相互查询 DNS 而无法应答任何请求的问题。

这种情况下需要配置两个位置:首先在路由器上取消「将本机地址插入 DNS 服务器列表」;并且在树莓派上配置不要使用从 DHCP 下发的 DNS 地址。这样就可以避免形成树莓派和路由器之间的循环查询。

单臂路由

迁出 DNS 服务之后,我尝试再次启动科学能力。但是让我感到震惊的是:这玩意启动科学的模式下居然不允许 DNS 直通模式。而如果我直接将 DNS 指向已经配置好的 DNS 服务器并启动隧道模式的话,DNS 查询仍然会失败。

那么,这是在逼我啊。之前一直想做而没有做的事情,要开始了。

我们即将进行的操作是配置一个单臂路由。即只使用一个网口的路由。之前没有在树莓派上配置是因为树莓派只有百兆网口。现在工控机上有千兆网口,不如我们再堆叠一个服务上去吧!

首先我们配置科学上网的能力,此处限于相关规定不再赘述。这个能力在本地提供端口 7892.

然后在路由器上为树莓派和工控机分配固定的 IP 地址。当然实际上我们之后将会禁用工控机的 DHCP 能力,所以此处为工控机分配的 IP 地址主要是为了预留该地址不被其他设备占用。

登录工控机,编辑网络配置为静态 IP 如下:

// in virtual file box!networkfs:/default.conf

IPv4 Configuration: Manual
Address: 192.168.16.120/24
Gateway: 192.168.16.1
DNS Servers: 192.168.16.110
Ignore automatically obtained routes: true
Ignore automatically obtained DNS parameters: true
Require IPv4 addressing for this connection: true

这可以保证我们的工控机不会因为路由器自动下发的路由配置导致网关错误进而无法联网的问题。

然后我们配置基本的路由能力。Archlinux 默认禁用 IP 转发,我们需要启用这个转发能力:

# sysctl -w net.ipv4.ip_forward=1
# sysctl -w net.ipv4.conf.all.send_redirects=0

要让工控机重启后自动启用这个能力,新建文件 /etc/sysctl.d/fwd.conf 并追加如下内容:

net.ipv4.ip_forward=1
net.ipv4.conf.all.send_redirects=0

此时我们就可以更新一下路由器配置,关闭自带的科学插件、更改默认 DNS 服务器为树莓派,并将默认网关指向工控机了:

// in virtual file routerfs:/dhcp.conf

Router IP: 192.168.16.1
Subnet: 255.255.255.0

DHCP IP Range: 192.168.16.2 -- 192.168.16.254
Lease time: 86400
Default gateway: 192.168.16.120
DNS Server: 192.168.16.110

保存后重启路由器应用更改。验证一下可以正常上网,我们继续配置。

透明代理

其实配置到科学上网能力后甚至哪怕不配置单臂路由,也是可以科学上网的。但是这样的话就需要手动配置各个设备去走这个路径。我的目标是完全无感的网络接入,所以还需要配置透明代理。

通过 iptables, 我们可以决定流入设备、从设备发出和流出设备的网络包如何运行。我们可以设计一个网络包的转发流程将流入设备的特殊包转交给科学工具处理,当然同时要注意不要把自己的网络炸掉。

首先,我们创建一个流程链,并将进入该链的数据包交给科学工具:

# iptables -t nat -N JMP
# iptables -t nat -A JMP -j REDIRECT -p tcp --to-ports 7892

当我们接到或即将发出 TCP 协议且目标端口为 80 或 443 的入站包时,转交给上述流程链处理:

# iptables -t nat -A PREROUTING -j JMP -p tcp --dport 80
# iptables -t nat -A PREROUTING -j JMP -p tcp --dport 443
# iptables -t nat -A OUTPUT -j JMP -p tcp --dport 80
# iptables -t nat -A OUTPUT -j JMP -p tcp --dport 443

但如果这个包来自局域网,那么不走代理链:

# iptables -t nat -I PREROUTING -j RETURN -d 192.168.0.0/16

如果出站数据包来自科学工具本身或者来自电视程序,则也不走代理链。好在这两个程序我们都指定了独立的用户,所以可以通过检查发包用户的方式实现:

# iptables -t nat -I OUTPUT -j RETURN -m owner --uid-owner clash
# iptables -t nat -I OUTPUT -j RETURN -m owner --uid-owner kodi

要保存这个配置,将 iptables 的配置导出到 /etc/iptables/iptables.rules:

# iptables-save -f /etc/iptables/iptables.rules

折腾了这么一圈之后,科学终于通过魔法的方式回归了这个八平半。但是这样做导致了一个关键问题:游戏加速器不能用了,因为默认网关不再是路由器本身,而是内网中的一个设备了。虽然按道理我仍然可以如法炮制地配置游戏加速,但是这玩意本身并不开放其工作原理,也并不(直接)提供 Linux 版本可用。尽管可以跑 OpenWrt 固件客户端而且手机控制端可以识别出来,但是似乎控制端并不能处理单臂路由的网络结构。这个问题我们将在之后再继续研究。

参考资料

参考资料
1 李家豪. 科学不能上网时的胡言乱语[EB/OL]. 豆沙工作室. (2022-03-06)[2022-03-12]. https://blag.dsstudio.tech/?p=4594
2 felixonmars et al. dnsmasq-china-list[DB/OL]. Gitee. (2022-03-13)[2022-03-13]. https://gitee.com/felixonmars/dnsmasq-china-list
3 CloudFlare. cloudflared[CP/OL]. Cloudflare. (2022-03-12)[2022-03-13]. https://developers.cloudflare.com/cloudflare-one/connections/connect-apps/install-and-setup/installation/
4 阿里云. DNS over HTTPs (DoH)[EB/OL]. 阿里云. (2021-07-01)[2022-03-13]. https://help.aliyun.com/document_detail/171664.html
  1. 老张博客说道:

    现在我的家里也出了问题,只要想科学,那网就卡的要命,不是全局。

发表回复

您的邮箱地址不会被公开。 必填项已用 * 标注

Your comments will be submitted to a human moderator and will only be shown publicly after approval. The moderator reserves the full right to not approve any comment without reason. Please be civil.