【25】无线双拨

目标

在我宿舍里,有无线校园网覆盖,校园网两个频段的 SSID 分别为 SUDA_WIFI(2.4 GHz),以及 SUDA_WIFI_5G(5 GHz)。在晚高峰时段,网络会存在 QoS 限流。参考家庭宽带的双拨操作,我想要在路由器上实现类似的多路无线中继以提升网速。

标题所述的无线双拨其实并非准确,无线中继和拨号没有任何关系。准确的叫法应该是双路/多路无线中继负载均衡,然而出于简洁和好听的目的,就用无线双拨/多拨这个名字好了。

准备 & 前情提要

关于前情提要,可以参考我去年写的一篇博客:【05】小米路由器mini 自制NAS。在这篇博客中,我使用一台小米路由器 mini,刷上 PandoraBox 系统以后实现各种骚操作。

硬件

一台小米路由器 mini。官网参数说明:https://www.mi.com/miwifimini/param 。OpenWrt 设备页面:https://openwrt.org/toh/xiaomi/mini

简单总结:

  1. 联发科 MT7620A 处理器
  2. 16 MB ROM,128 MB RAM
  3. 2.4 GHz 以及 5 GHz 天线均为 2*2 MIMO
  4. WAN * 1,LAN * 2,USB 2.0 * 1

固件

由于在 PandoraBox 中,无线中继存在 bug(参考博客【05】),因此这次准备采用更加开放的 OpenWrt 系统。在官网下载固件,链接:

稳定版(18.06.4):https://downloads.openwrt.org/releases/18.06.4/targets/ramips/mt7620/

开发预览版:https://downloads.openwrt.org/snapshots/targets/ramips/mt7620/

在页面中找到 miwifi-mini-squashfs-sysupgrade.bin 或是 xiaomi_miwifi-mini-squashfs-sysupgrade.bin,下载固件并上传到路由器。由于我在稳定版中遇到了某些奇怪的 bug,故采用了开发预览版。

刷机流程可以参考 OpenWrt 上的设备页面,这里提供简单总结:

(如果是原生固件的话,首先需要使用小米官方工具解锁 SSH 权限,详情参考博客【05】

首先确定固件所在分区名。使用指令 cat /proc/mtd 可以查看所有分区的名字。在我的设备上,小米原生系统的固件分区名为 OS1,PandoraBox 和 OpenWrt 的固件分区名为 firmware

因此,原生固件刷 PandoraBox / OpenWrt 的指令为:

mtd -r write /path/to/the/firmware.bin OS1

PandoraBox / OpenWrt 互刷指令:

mtd -r write /path/to/the/firmware.bin firmware

如果系统保持不变,则可以直接使用 LuCI 网页管理界面升级。

注:PandoraBox 刷机/重置完成后默认打开 WIFI,而 OpenWrt 默认不启用 WIFI,记得准备网线。

软件

需要安装的软件包:

  1. luci luci-i18n-base-zh-cn luci-theme-material
  2. mwan3 luci-app-mwan3 luci-i18n-mwan3-zh-cn
  3. coreutils-base64 curl

第一组是基础软件包,分别是网页管理界面 luci,中文语言包和 material 主题。预览版系统并不内置 luci,需要自行安装。

第二组是负载均衡所需的 mwan3,以及 luci 中的 mwan3 管理界面。

第三组是自动网关登录脚本的依赖,按需安装。

双路无线中继负载均衡

无线中继

无线中继的方法在博客【05】中已经有过说明。由于两张网卡均为 2*2 MIMO,因此无法对单个频段做多路中继,只能在两张网卡上分别中继一路信号。假如有支持更多 MIMO 的网卡,可以试着更多路的无线中继。

流程总结:

  1. 无线 选项卡中,分别在 2.4 GHz 和 5 GHz 网卡上点击添加,在无线列表中加入对应的网络。这个操作会新建一个网卡,防火墙区域 需要选在 wan 网卡所在的区域。重置无线配置 选项勾选的话会删除现有的 WIFI,如有需要可以勾选。
  2. 基本设置 选项卡中确保模式为 客户端 Client 模式,在 无线安全 选项卡中设置好网络的账号密码。信号的发射功率上限可以通过 高级设置 中把国家代码设置成 US 来提升。
  3. 分别在 2.4 GHz 和 5 GHz 网卡中添加新的 WIFI,确保模式为 接入点 AP。当然,如果没有 2.4 GHz 频段的需要的话,也可以只使用 5 GHz 的网络避免信道拥挤。(如果第一步没有删除之前存在的网络,则不用再做这一步)
  4. 至于 IPv6 的中继,需要在网卡里另外手动添加 DHCPv6 客户端以接通 IPv6 上游。然而我校校园网的 IPv6 分配策略貌似是一个设备只分配一个地址,而非 DHCP-PD,NDP 代理也不起作用,因此中继 IPv6 唯一的方法是丑陋的 NATv6。不过 IPv6 目前还并非刚需,用 NAT6 还不如不用。
  5. 这时候你应该已经能够上网了。当然,只要有一张网卡已经加入无线网络,所有的 LAN 口 / WIFI 就都能够访问网络了。当设置两张网卡都进行无线中继的时候,网络流量会走哪个无线就取决于路由表了。由于不清楚固件底层的实现,路由器会使用哪个无线网络在我看来基本是个玄学。但这不重要,马上就来解决。

负载均衡

在两张网卡都加入无线网络后,就可以使用 mwan3 进行负载均衡了。进入 网络 -> 负载均衡 选项卡进行设置。在这底下一共有 6 个选项卡,我们一个一个来设置。

  1. 全局。这个不用动,跳过即可。
  2. 接口。这里添加的是所有参与负载均衡的网络接口。我在设置无线中继的时候,SUDA_WIFI 对应的网络接口名为 wwan24SUDA_WIFI_5G 对应的网络接口名为 wwan,因此需要把 wwanwwan24 添加到这里。配置中需要设置 跟踪的主机或 IP 地址,也就是用来检测网络是否可用的地址,可以设置成公共 DNS。mwan3 会自动每隔一段时间 ping 一下这一个或多个地址,在失败一定次数(可以自行设置该参数)后认为接口离线。 interface
  3. 成员。这里设置的是每个接口的权重。权重分为两种,一个叫做 跃点数,另一个叫做 比重。权重的计算规则为, “拥有较低跃点数的成员将会被优先使用,拥有相同跃点数的成员把流量进行负载均衡”。这里,我们想要的是两个接口做负载均衡,因此都把跃点数设置为 1,而比重分别设置为 1 和 3(更多的流量分给比较快一点的 SUDA_WIFI_5G)。 member
  4. 策略。这里是负载均衡的策略,用于对接口成员进行分组。由于这里只需要对两个接口做负载均衡,因此只需要一条策略,把两个接口成员添加进去即可。 policy
  5. 规则。这里用于指定哪些流量使用哪些策略。我们并没有这些需求,因此简单把所有流量都转发到刚刚那条策略即可。如果有特定流量需求的则可以在这里设置。 rule
  6. 通知。在这里可以编写一个自定义脚本 /etc/mwan3.user,用于指定特定接口事件时的操作。我们可以利用这个脚本做自动接口登录,详见下文

完成这一系列设置后,我们的网络就成功进行了多线负载均衡。可以进入到 状态 -> 负载均衡 选项卡查看当前接口状态,流量分配情况,以及进行网络诊断。另外,也可以根据 网络 -> 接口 页面中各个接口的流量来判断负载均衡是否在正常运作。

对于更详细的配置说明,可以参考官方文档:https://openwrt.org/docs/guide-user/network/wan/multiwan/mwan3

自动任务

开机脚本

系统 -> 启动项 -> 本地启动脚本 中可以设置启动脚本。我想要的是每次开机都自动向网关发送一个登录请求,因此在启动脚本中添加以下指令:

1
sleep 30 && /root/wg-login.sh

每次开机都会等待 30 秒,等待网络设备就绪后执行登录脚本。

登录脚本可以参考该 GitHub 项目:https://github.com/SteveHawk/suda-wg

计划任务

系统 -> 计划任务 中可以轻松编辑 crontab 中的计划任务。

1
2
3
4
5
6
7
# Login suda wifi at 27min of every hour
27 * * * * /root/wg-login.sh

# Reboot at 3:33am every day
# Note: To avoid infinite reboot loop, wait 70 seconds and touch a file
# in /etc so clock will be set properly to 3:34 on reboot before cron starts.
33 3 * * * sleep 70 && touch /etc/banner && reboot

两个计划任务,分别是每小时 27 分登录一次网关,以及每天 3:33 重启路由器。为了避免重启死循环,需要使用如上这样的重启指令。

不过我已经禁用了自动登录任务,因为它可以由接下来这个更好的方案来取代。

接口下线事件

仔细想想,登录网关这事情完全没必要每个小时执行一次,只需要在检测到接口下线的时候发送登录请求不就行了嘛。mwan3 的 通知 功能就正好可以用来实现这个目的。

通知页面的这个脚本 /etc/mwan3.user 会在 netifd hotplug 接口事件时执行。换句话说,任何一个接口上线 / 下线事件都会触发这个脚本执行一次。这个功能的本意是用来在接口上线 / 下线的时候给用户发送邮件等提醒,不过我们也可以拿来做自动网关登录。

脚本能够获取到三个环境变量,分别是 $ACTION$INTERFACE$DEVICE$ACTION 共有四种值,分别是 ifupifdownconnecteddisconnected。而 $INTERFACE$DEVICE 可以分别用来获得当前触发脚本的接口和设备名。

利用这几个环境变量,可以编写出如下脚本:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
#!/bin/sh
#
# This file is interpreted as shell script.
# Put your custom mwan3 action here, they will
# be executed with each netifd hotplug interface event
# on interfaces for which mwan3 is enabled.
#
# There are three main environment variables that are passed to this script.
#
# $ACTION
#      <ifup>         Is called by netifd and mwan3track
#      <ifdown>       Is called by netifd and mwan3track
#      <connected>    Is only called by mwan3track if tracking was successful
#      <disconnected> Is only called by mwan3track if tracking has failed
# $INTERFACE	Name of the interface which went up or down (e.g. "wan" or "wwan")
# $DEVICE	Physical device name which interface went up or down (e.g. "eth0" or "wwan0")

if [ $ACTION == "disconnected" ]
then
    if [ $INTERFACE == "wwan" ]
    then
        mwan3 ifdown wwan24
        mwan3 ifup wwan
        source /root/wg-login.sh
        logger "Interface wwan execute login."
    elif [ $INTERFACE == "wwan24" ]
    then
        mwan3 ifdown wwan
        mwan3 ifup wwan24
        source /root/wg-login.sh
        logger "Interface wwan24 execute login."
    fi
    mwan3 ifup wwan
    mwan3 ifup wwan24
fi

我们想要做的事情,是在每次接口下线的时候,向这个接口发送网关登录请求。但是由于接口下线事件触发的时候,mwan3 会自动把路由表导向另一个在线的接口,这让我们无法把登录请求发向已下线的接口。所以我在这个脚本里做的,就是在一个接口下线的时候,先手动让另一个接口下线并让当前接口上线,这个时候路由表就会指向实际下线的这个接口;此时顺势发送网关登录请求,然后再让两个接口恢复上线。

于是,在任何一个接口下线的时候,路由器都会自动发送登录请求让它重新上线。只要不是触发了网关的保护措施(我校网关貌似有连接时长限制,超时不会断开,但是会没网),我的路由器就可以轻松实现 24 小时在线啦。

DNS 重绑定攻击の坑

最后,需要提一嘴踩到的一个坑。先来讲讲发生了什么。在刷完 OpenWrt 并做完单路无线中继后,我试着访问校园网网关的页面,但是怎么也上不去。到系统日志里翻看了一下,出现了非常可疑的记录:

1
daemon.warn dnsmasq[904]: possible DNS-rebind attack detected

欸,什么是 DNS 重绑定攻击DNS rebinding attack)?

简单来说,在执行 DNS 重绑定攻击的时候,攻击者先会向用户发送一个 TTL 非常短的解析记录,解析到包含恶意代码的网站上。恶意代码执行后会创建第二个 DNS 请求(因为上一个记录的缓存已经超时),这时攻击者可以返回一个内网 IP 的解析记录。这样攻击者就可以轻松访问到原本无法访问的内网内容了。

为什么我会踩到这个坑?我们来仔细看一下 DNS 重绑定攻击的流程,其中有两个可以填的漏洞,一个是超短 TTL 的 DNS 记录,另一个是解析到内网 IP 的 DNS 记录。超短 TTL DNS 记录的防护可能在浏览器端进行会比较好,而路由器端能做的就是拦截解析到内网 IP 的 DNS 记录。

巧的是,我校校园网网关地址 a.suda.edu.cn 解析得到的地址是 10.9.0.30,这是一个 A 类的内网 IP 地址。真相大白。

解决方案有两种,一个是给域名添加白名单,另一个是禁用防护。因为我们校园网内不止一个内网网站,因此直接禁用防护是更好的选择。在 网络 -> DHCP/DNS -> 基本设置 选项卡中,可以看到有一个 重绑定保护 的选项。取消勾选即可。


至此,整个路由器的配置就完成了。能够 24 小时在线,不必每个设备都手动登录网关,还有双路负载均衡,这也太爽了!😍


本文阅读量
本站访客量