使用 openwrt 绕过 AT&T 光纤网关

Yicheng 于 2020-08-12 发布

换了 AT&T 的光纤有一段时间了,速度确实快,但是有个很烦的问题:AT&T 强行每个月收我 $10 的设备租赁费,租给我一个他们的路由器盒子(RGW,Residential GateWay)。

对于 AT&T 来说,这个盒子可以帮他们管理用户的上网、电话、电视等服务,是个挺重要的设备。但是对于我来说,我只需要他们的网络,其他什么电话、电视服务我都不用。而这个 RGW 作为一个无线路由器来说,真的是非常不称职。

RGW 的各种槽点

用了一段时间以后,我发现这个盒子有一堆问题:

1. NAT 表被限制在 8192 个会话

大部分时候这个限制还够用,但是当我同时下载多个 PT/BT 种子的时候,NAT 表会被填满,然后一些连接就会被强制关闭。

最烦的是,有些登录后需要保持 WebSocket 连接的服务(比如一些实时监控面板),会因为连接被关闭而需要重新登录。用着用着突然就掉线了,真的很烦。

2. 缺少一个可以用的防火墙

基本上就是个摆设,想做点高级配置根本做不了。

3. 缺乏管理功能

比如说,我想把所有通过 WiFi 连接的 IoT 设备(智能灯泡、插座之类的)和我的内网隔离开来,这个路由器根本做不到。VLAN?不存在的。

4. 无线信号极其糟糕

这个是最让我抓狂的。大部分时候可以用,但是偶尔会抽风,表现为有信号但是上不了网。手机显示连着 WiFi,信号满格,但就是打不开网页。重启路由器才能解决,简直了。

尝试过的解决方案

路由器本身提供了 IP Passthrough 功能,可以在后面接一个自己的路由器。这个可以解决防火墙和无线信号的问题,但是 NAT 表限制和管理功能的问题依旧存在,因为流量还是要经过 RGW。

搜索了一圈以后,发现了几种绕过 RGW 的方法:

方案 1:降级固件获取 root 权限

通过降级路由器固件的方法来获得 root 权限,然后拿到 AT&T 的认证凭据(credentials)。

好处: 可以彻底摆脱 RGW,把这个破盒子扔一边。

坏处: 配置复杂,网上的案例大多是通过 X86 Linux 路由机器来完成的。而且我没有找到适用于我当前路由器固件版本的降级固件,不确定能不能用,有刷成砖的风险。万一刷砖了,AT&T 还得收我设备损坏费,得不偿失。

参考:root exploit

方案 2:转发 802.1X 验证包

通过路由器转发 AT&T 网关的 802.1X 验证包来进行身份验证。

好处: 配置看上去简单一些,只需要转发特定的数据包,网上也有现成的代码可以用。RGW 还是要保留,但是可以关掉它的路由功能,只用来做认证。

坏处: RGW 还是得留着,虽然可以关掉电源,但万一断电或者断网需要重新认证的时候,还得把它开起来。

参考:

方案 3:前置交换机

就是把 ONT(光猫)、RGW 和自己的路由器一起连到一个交换机上,先让 AT&T 盒子通过验证,然后拔掉。

好处: 看上去最简单,不需要改任何配置。

坏处: 节奏没掌握好的话,连不上网。而且每次断电或者断网后需要重新验证,都要手动操作一遍,太麻烦了。

参考:switch 方案

我的选择:方案 2

综合考虑下来,我选择了方案 2。虽然 RGW 还得留着,但至少配置相对简单,而且风险小。

开始配置

在开始之前,需要先准备好 RGW 的两个关键信息:

这两个信息很容易找到,登录进 RGW 的管理界面,或者直接看机器背面的标签就有。

物理连接

AT&T 安装光纤的时候,会给你配三个设备:

  1. 分光盒:装在入户墙上,把光纤分出一根引入室内
  2. ONT(光猫):接入引入室内的光纤,输出一根 RJ45 网线
  3. RGW(路由器):ONT 口接 ONT 输出的网线

现在要加入自己的 OpenWrt 路由器,连接方式是这样的:

  1. 分光盒:不动
  2. ONT:不动
  3. OpenWrt 路由器:WAN 口接 ONT 的网线,随便选一个 LAN 口(我用的是 LAN 0)连接 RGW
  4. RGW:ONT 口接 OpenWrt 的 LAN 0

简单来说,就是把 OpenWrt 路由器插在 ONT 和 RGW 之间。

网络配置

修改 WAN 口配置

编辑 /etc/config/network,主要是把 macaddr 改成 RGW 的 MAC 地址。这样 AT&T 的服务器会以为还是原来的 RGW 在连接。

peerdnslist dns 可以不设置,用默认的运营商 DNS。我这里用了 Google 的 DNS,wan6 也是一样。

config interface 'wan'
        option ifname 'eth1.2'
        option proto 'dhcp'
        option macaddr 'xx:xx:xx:xx:xx:xx'  # 改成你的 RGW MAC 地址
        option delegate '0'
        option peerdns '0'
        list dns '8.8.8.8'
        list dns '8.8.4.4'

修改 WAN6 口配置(IPv6)

如果需要 IPv6 地址,就得配置 wan6 接口。这里有个坑,AT&T 接受的 clientid 格式很特殊,是以 00:02:00:00:0d:e9:30:30:31:45:34:36:2d: 为前缀,后面跟着序列号的十六进制代码。

可以用 Python 简单转换一下:

>>> ':'.join('%02x' % ord(c) for c in '12345A123456')
'31:32:33:34:35:41:31:32:33:34:35:36'

把输出的字符串替换到下面配置里的 xx:xx... 部分:

config interface 'wan6'
	option proto 'dhcpv6'
	option reqaddress 'try'
	option reqprefix 'auto'
	option clientid '00:02:00:00:0d:e9:30:30:31:45:34:36:2d:xx:xx:xx:xx:xx:xx:xx:xx:xx:xx:xx:xx:xx:xx'
	option ifname 'eth1.2'
	option peerdns '0'
	list dns '2001:4860:4860::8888'
	list dns '2001:4860:4860::8844'

添加 VLAN

对于一般的路由器,OpenWrt 会自动创建两个 VLAN,编号 1 和 2。我们需要新建一个编号为 3 的 VLAN 给 RGW 用。

我的路由器 LAN 口对应的是 eth0,对应的是 port5。所以 option 里面加上 ports '5t 0'

config switch_vlan
        option device 'switch0'
        option vlan '3'
        option ports '5t 0'
        option vid '3'

config switch_port
        option device 'switch0'
        option port '0'
        option pvid '3'

记得要把 vlan1 或者 vlan2 里面的 ports 0 去掉,因为 port 0 已经分配给 vlan3 了。

添加接口(可选)

这一步只是为了方便,可以不加。

新建一个 ont 接口指向 eth1.2(也就是路由器 WAN 口),新建一个 rgw 接口指向 eth0.3(也就是刚新建的 VLAN3):

config interface 'ont'
        option proto 'none'
        option ifname 'eth1.2'

config interface 'rgw'
        option proto 'none'
        option ifname 'eth0.3'

安装 eap_proxy

这个是关键,用来转发 802.1X 认证包的。

一开始我用了 Python 版本的 eap_proxy,可以用,但是大概需要 10MB 的空间来安装 Python 和依赖。如果路由器剩余空间不多,这个就不太合适。

后来我找到了 Go 版本的 goeap_proxy,编译出来的二进制文件只有几 MB,而且不需要任何依赖。

根据自己的路由器型号来改变 GOARCH,编译很简单:

env GOOS=linux GOARCH=arm go build -v github.com/pyther/goeap_proxy

把编译好的可执行文件拷贝到路由器的 /usr/bin/goeap_proxy

启动脚本

新建一个 /etc/init.d/goeap_proxy 文件:

#!/bin/sh /etc/rc.common
START=25
USE_PROCD=1

boot()
{
    ubus -t 30 wait_for "network.interface.ont" "network.interface.rgw"
    rc_procd start_service
}

start_service()
{
    procd_open_instance
    # 如果程序在 600 秒内退出,隔 30 秒重启
    procd_set_param respawn ${respawn_threshold:-600} ${respawn_timeout:-30} ${respawn_retry:-0}
    procd_set_param stdout 1
    procd_set_param stderr 1
    procd_set_param command /usr/bin/goeap_proxy
    # 这里也可以直接用 eth1.2 和 eth0.3,可以不用新建 ont 和 rgw 接口
    procd_append_param command -if-wan "$(uci get "network.ont.ifname")"
    procd_append_param command -if-router "$(uci get "network.rgw.ifname")"
    procd_close_instance
}

service_triggers()
{
    procd_add_reload_trigger "goeap_proxy" "network"
}

然后启动服务:

/etc/init.d/goeap_proxy start

等一会儿,OpenWrt 应该就能获取到网络地址并且可以上网了。

最后,设置开机自启:

/etc/init.d/goeap_proxy enable

后续优化

eap_proxy 其实只是用来联网验证的,连上网以后,理论上可以关掉。RGW 在连上网之后也可以关掉电源。

不过这样做的话,如果断电或者 AT&T 挂掉了,或者路由器重启了,就得去手动打开 RGW 电源重新验证,挺麻烦的。

我现在的做法是直接拔掉了 RGW 的电源,毕竟这玩意儿开着待机就有 10W 左右的功率,一年下来也是一笔电费。反正我这边很少断电,就算断电了,大不了再把 RGW 插上电验证一下。

为了防止断电造成断网,我在路由器前面挂了个 UPS。目前稳定运行了几个星期,一次都没断过网,完美。 终于可以用自己的路由器了,NAT 表限制、防火墙、VLAN 这些问题全都解决了,爽!