Back
Featured image of post TPLink-R479GP-AC小身板也有大作用

TPLink-R479GP-AC小身板也有大作用

春节回老家,偶尔翻看一些资料的时候因网络原因众多网站打不开,此事颇为恼人。不由怀念起在家小机柜那点小设备,无奈现在手上空空,闲置的软路由也没带回来,巧妇难为无米之炊。当前这套网络是TPLink AC+AP方案,便对这个AC有了点想法。也就它有点算力,有个系统了吧?:)

网络背景

这是几年前给家里多层楼选择的一套TPLink方案(其实是我先在深圳用淘汰的方案给回了老家使用,吼吼)。有一个AC叫TL-R479GP,为了方便部署,同时支持8口POE供电,连接两个主AP(TL-AP1758GC)和各楼层的一些补充AP(86面板AP)。这不由的回想起,当时在京东下单这一套,也着实不便宜,然后发现1688上居然有几百块线差价,果断退掉去1688买,太香了。当然,很久没去了解了,不清楚如何状况,但从此内心觉得国内有些产品各渠道加价真高啊!

打入R479GP的内部

获得R479GP的ssh权限

不需要复杂的破解,网上已经有几种获得ssh的教程了。主要分两类:

  • 通过修改配置(ssh的用户名/密码)并且重新打包导入配置,获得ssh的登录。
  • 通过默认密码的方式进入。难点在计算默认密码,好在有前人经验,只有一行命令。

我的R479GP最近找客服要了最新固件(1.1.5 Build 20201110 Rel.72597)。感谢京东,客服甚至没问订单没管我是否在他们那购买的就果断发了固件过来,品牌还是不错的嘛:)刷完后我看人家教程一下心惊,听说后续版本有修复一些ssh漏洞,可别搬起石头砸了自己脚啊!所幸后续一切顺利。只要这样:

  1. 在设置中[系统工具]->[诊断工具]->[故障诊断]->[开启诊断模式] 即可打开ssh通道,默认端口为33400
  2. root密码为LAN口mac地址md5后的前8位(有些版本是前16位)。LAN的MAC地址在基本设置->LAN设置里可以看到。这里假设我的是CC-08-07-06-05-04,然后可以如下一行命令获得密码:
1
2
echo "CC-08-07-06-05-04" | tr -d '-' | tr -d '\n' | md5 | cut -c 1-8
42df9037

你以为这就结束了吗,太理想了,有用户名和密码后,你登录会神奇的报错,这个我之前没见过啊:

1
Unable to negotiate with 192.168.0.1 port 33400: no matching key exchange method found. Their offer: diffie-hellman-group1-sha1,diffie-hellman-group14-sha1,kexguess2@matt.ucc.asn.au

原因或许是因为R479GP系统的ssh服务端(dropbear)用的比较旧的版本了。要想能正常登录,需要添加一系列参数才可以。

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
❯ ssh -o HostKeyAlgorithms=+ssh-rsa -o PubkeyAcceptedAlgorithms=+ssh-rsa \
-o KexAlgorithms=diffie-hellman-group1-sha1  root@192.168.0.1 -p 33400
root@192.168.0.1's password:


BusyBox v1.22.1 (2020-11-10 20:05:06 CST) built-in shell (ash)
Enter 'help' for a list of built-in commands.

  _______                     ________        __
 |       |.-----.-----.-----.|  |  |  |.----.|  |_
 |   -   ||  _  |  -__|     ||  |  |  ||   _||   _|
 |_______||   __|_____|__|__||________||__|  |____|
          |__| W I R E L E S S   F R E E D O M
 -----------------------------------------------------
 BARRIER BREAKER (Barrier Breaker, r96964)
 -----------------------------------------------------
  * 1/2 oz Galliano         Pour all ingredients into
  * 4 oz cold Coffee        an irish coffee mug filled
  * 1 1/2 oz Dark Rum       with crushed ice. Stir.
  * 2 tsp. Creme de Cacao
 -----------------------------------------------------
root@TP-LINK:~#

随便看看

既然进来了,我们当然要看看这台设备的信息。

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
root@TP-LINK:~# uname -a
Linux TP-LINK 3.3.8 #1 Tue Nov 10 20:09:13 CST 2020 mips GNU/Linux

root@TP-LINK:~# cat /etc/openwrt_release
DISTRIB_ID="OpenWrt"
DISTRIB_RELEASE="Barrier Breaker"
DISTRIB_REVISION="r96964"
DISTRIB_CODENAME="barrier_breaker"
DISTRIB_TARGET="ar71xx/generic"
DISTRIB_DESCRIPTION="OpenWrt Barrier Breaker 14.07"
DISTRIB_TAINTS="no-all no-ipv6 busybox"

root@TP-LINK:~# cat /etc/openwrt_version
14.07

原来是openwrt的系统啊,MIPS架构。再瞧瞧CPU:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
root@TP-LINK:~# cat /proc/cpuinfo
system type		: Qualcomm Atheros QCA956X rev 0
machine			: TP-LINK TL-R4299G 2.0
processor		: 0
cpu model		: MIPS 74Kc V5.0
BogoMIPS		: 373.55
wait instruction	: yes
microsecond timers	: yes
tlb_entries		: 32
extra interrupt vector	: yes
hardware watchpoint	: yes, count: 4, address/irw mask: [0x0000, 0x0ff8, 0x0ff8, 0x0ff8]
ASEs implemented	: mips16 dsp
shadow register sets	: 1
kscratch registers	: 0
core			: 0
VCED exceptions		: not available
VCEI exceptions		: not available

我去啊,高通芯片,怎么解读它的强弱,我们看看GPT(其实这次让Gemini客串了)怎么说:

Info

这台网络设备的 CPU 性能一般。它使用的是 Qualcomm Atheros QCA956X 芯片,该芯片于 2013 年发布。它是一款单核 MIPS 74Kc V5.0 处理器,主频为 373.55 MHz。

MIPS 74Kc 是 MIPS 公司于 2002 年发布的一款 32 位 RISC 处理器架构。它是一款低功耗、低成本的处理器,适用于嵌入式系统。

QCA956X 芯片主要用于网络设备,例如路由器和交换机。它还可用于其他嵌入式系统,例如家用电器和工业控制设备。

这台网络设备的 CPU 性能足以满足大多数网络应用的需求。但是,对于要求更高的应用,例如视频流或游戏,它可能不够强大。

以下是 QCA956X 芯片的一些性能指标:

  • 主频:373.55 MHz
  • 指令集:MIPS 32
  • 缓存:32 KB L1 指令缓存、32 KB L1 数据缓存
  • 内存:支持 DDR2 和 DDR3 内存
  • 外围设备:支持多种外围设备,例如 USB、Ethernet 和 PCIe
我不由的担心起来它这小身板还能不能被折腾一下。 再看看内存:

1
2
3
4
5
6
7
8
9
root@TP-LINK:~# cat /proc/meminfo
MemTotal:         126464 kB
MemFree:           58968 kB
Buffers:            7960 kB
Cached:            32548 kB
SwapCached:            0 kB
Active:            28672 kB
Inactive:          19708 kB
Active(anon):      10584 kB

128MB的家庭,让我又想起被嫌弃只有几G内存的软路由静静在躺在家里角落许久,是我的不该啊:) 就这点内存,跑个大点的程序,是不是系统就各种OOM了,想想都怕! 最后看一眼存储空间:

1
2
3
4
5
6
7
8
9
root@TP-LINK:~# df -h
Filesystem                Size      Used Available Use% Mounted on
rootfs                   61.8M    732.0K     61.0M   1% /
/dev/root                 9.3M      9.3M         0 100% /rom
tmpfs                    61.8M      4.0M     57.7M   7% /tmp
root                     61.8M    732.0K     61.0M   1% /tmp/root
overlayfs:/tmp/root      61.8M    732.0K     61.0M   1% /
/dev/mtdblock7            2.0M    712.0K      1.3M  35% /tmp/userconfig
tmpfs                   512.0K         0    512.0K   0% /dev

什么!只有61M,并且还不是都可持久化。网上查了一下,可持久化保存的只有/tmp/userconfig这里,其它重启后会被重置。不要怀疑你的眼睛,真的是只有1.3MB呢? 怎么办,我程序放哪啊,还怎么折腾啊?

让v2ray住进小房子

编译自己的v2ray

想要更好的网络条件,我们要借助v2ray等,幸好它是golang开发的,交叉编译出MIPS版本也非难事。可能你也像我一样对交叉编译经验不多,特别是上面GOOS是啥,是linux吗?不会是openwrt吧?哈哈,你可以这样: go tool dist list看到go所支持的系统和架构。

最后我们的编译命令是:

1
GOMIPS=softfloat CGO_ENABLED=0 GOOS=linux GOARCH=mips go build -o $HOME/v2ray -trimpath -ldflags "-s -w -buildid=" ./main

编译后赶紧看一下生成的二进制有多大。31M,完蛋了,放在/tmp/userconfig下是不可能了。所幸你也发现了/tmp目录下还有57M剩余空间,希望点燃了。 这时你想把这编译的二进制SCP过去就好了吧,对不起,dropbear不支持scp。我发现这个系统里有wget命令(curl没有),这是一个积极信号。

我们顺手先在编译机起一个http服务器(在对应二进制目录下),编译机当然有python啦:

1
python3 -m http.server

然后在R479GP下:

1
2
3
4
5
6
7
8
9
# 只有/tmp下有点空间放东西了
cd /tmp
# 下载我们编译好的v2ray,注意换成你自己的ip/port
wget http://192.168.0.153:8000/v2ray

# 顺便看一下是否能执行
root@TP-LINK:/tmp# ./v2ray version
V2Ray 5.13.0 (V2Fly, a community-driven edition of V2Ray.) Custom (go1.20 linux/mips)
A unified platform for anti-censorship.

搞定了编译和二进制问题啦。

构建自己的v2ray配置

我基本没怎么使用过v2ray,它的配置和其它一些类似软件不太一样,服务器也不通用。我随便找了一个简单的模版,对其进行了某些修改:

 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
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
{
    "log": {
        "loglevel": "debug"
    },
    "routing": {
        "domainStrategy": "AsIs",
        "rules": [
            {
                "type": "field",
                "ip": [
                    "223.5.5.5/32",
                    "119.29.29.29/32",
                    "180.76.76.76/32",
                    "114.114.114.114/32"
                ],
                "outboundTag": "direct"
            }
        ]
    },
    "inbounds": [
        {
            "listen": "0.0.0.0",
            "port": "1080",
            "protocol": "socks",
            "settings": {
                "auth": "noauth",
                "udp": true,
                "ip": "0.0.0.0"
            }
        },
        {
            "listen": "0.0.0.0",
            "port": "1081",
            "protocol": "http"
        }
    ],
    "outbounds": [
        {
          "tag": "proxy",
          "settings": {
            "vnext": [
              {
                "address": "<vws-url....>",
                "users": [
                  {
                    "alterId": 0,
                    "id": "<id>",
                    "level": 0,
                    "security": "auto"
                  }
                ],
                "port": "<port>"
              }
            ]
          },
          "streamSettings": {
            "security": "none",
            "wsSettings": {
              "headers": {
                "host": ""
              },
              "path": "<...>"
            },
            "network": "ws"
          },
          "mux": {
            "enabled": false,
            "concurrency": 8
          },
          "protocol": "vmess"
        },
        {
          "protocol": "freedom",
          "settings": {
            "userLevel": 0,
            "domainStrategy": "UseIP"
          },
          "tag": "direct"
        },
        {
          "settings": {
            "response": {
              "type": "none"
            }
          },
          "protocol": "blackhole",
          "tag": "block"
        }
    ]
}

其中有几处主要修改:

  • inbounds里修改了listen的地址为0.0.0.0,方便内网其它使用。
  • outbounds里的配置我不太清楚怎么写。我开始也不知道,做了两种尝试,它们都是有效的。

从订阅中获取

我们手上可能有支持v2ray的订阅,但具体到v2y配置应该怎么写,我是不懂也懒得研究。但我发现有个工具v2sub可以拉取v2ray的订阅并且生成配置,它居然也是golang的,这可太喜欢了(意味着使用方便)。

1
sudo ./v2sub

在两轮交互式输入下,它会产生/etc/v2ray.json/etc/v2sub.json文件。其中/etc/v2ray.json里的那一段outbounds就是我们要抄过来的。题外话一下,个人感觉工具非必要不需要交互式,还是带参数更加好用一些。又没有复杂度又没有太多上下文,你交互式有啥意义呢? 当然,作为经历过喝水不忘挖井教育的我们,还是感谢一下作者。

从V2rayU中获取

我们都知道很多工具支持订阅,我试着在MacOS下安装了V2rayU,在正常操作能使用后(这步确认了代理服务器没问题),我们可以在菜单中点开查看到它的config.json。同样我们可以复制出我们想要的outbounds那一段。

验证

到此处,程序和配置都有了,你可以把生成的配置放在前面http-server的目录下,然后同样的在R479GP中用wget收入囊中。这里我默认你会了,并且这个配置叫config.json,然后我们只需要这样:

1
./v2ray run -c config.json &

便启动了一个v2ray客户端,它监听在1080(socks)和1081(http)。之后,就可以在本地浏览器上通过设置你的SwitchyOmega插件,添加一个sock5或http协议的proxy,让某些网站走它即可。这期间从日志上看v2ray有一些报错,但不影响使用。发现看网站如google等,CPU/Mem也没多大负载。或许v2ray确实更轻量和高效一点吧:)

如果到这里就结束,那和我们在电脑上直接开一个软件也没多大区别,必须得有后手。

断电就无怎么可以接受?

上面已经描述过,在这台小身板的R479GP中,只有1.3M的空间是永久存储东西的。我们可以进去看看,发现有个文件/tmp/userconfig/etc/rc.local文件。

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
root@TP-LINK:/tmp/userconfig/etc# cat  rc.local
# Put your custom commands here that should be executed once
# the system init finished. By default this file does nothing.

irq_balance()
{
    local irqnum=`cat /proc/interrupts | awk -F: '/eth/ {print $1}' | sed 's/[^0-9]//g'`

    [ "1" == `cat /proc/irq/${irqnum}/smp_affinity` ] && {
        local core_num=`cat /proc/cpuinfo | grep -w processor | awk 'END{print NR}'`
        local mask=`awk "BEGIN{f=lshift(1,$core_num)-1; print f}"`
        echo `printf %x $mask` > /proc/irq/${irqnum}/smp_affinity
    }
}
irq_balance


exit 0

写得挺友好,Put your custom commands here that should be executed once。那么我们可以放点什么进来呢?有些文章说在这里sleep一下,然后放上之前我们的一系列下载配置和启动程序的命令,虽然可行,但不优雅。我想要守护进程,你不给我这机制,那么我就定时执行,于是乎借助crontab来搞:

1
2
# add crontab for start v2ray
echo "*/1 * * * * echo \"check v2ray process at \`date\`\" >> /tmp/result.crontab 2>&1 && sh /tmp/userconfig/start-v2ray.sh" | crontab -

为了防止日程月累打爆日志,我们得小心不去不断追加日志和打印不必要的输出,当然因为/tmp/目录也是易失的,定时重启也会缓存这种情况。之后将以上内容写到上述rc.local文件的exit 0之前。接下来编写我们的脚本:

 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
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
#!/bin/sh

# 定义工作目录和服务器URL
WORK_DIR="/tmp"
HTTP_SERVER_URL="http://192.168.0.153:8000"
V2RAY_BIN="v2ray"
CONFIG_FILE="config.json"

# 预期的v2ray文件的MD5值
EXPECTED_MD5="2c2adf543c9008f3d8976c71c14b483e"

# 下载v2ray和配置文件
download_files() {
    echo "Downloading v2ray and config file..."
    wget -O "${V2RAY_BIN}" "${HTTP_SERVER_URL}/${V2RAY_BIN}"
    wget -O "${CONFIG_FILE}" "${HTTP_SERVER_URL}/${CONFIG_FILE}"

    # 检查MD5校验值
    if echo "${EXPECTED_MD5}  ${V2RAY_BIN}" | md5sum -c -s; then
        echo "MD5 check succeeded."
    else
        echo "MD5 check failed, exiting script."
        exit 1
    fi

    # 赋予v2ray执行权限
    chmod +x "${V2RAY_BIN}"
}

# 检查v2ray进程是否在运行
check_v2ray() {
    return `ps | grep "[v]2ray" | grep -v grep`
}

# 启动v2ray进程
start_v2ray() {
    echo "Starting v2ray process..."
    ./"${V2RAY_BIN}" run -c "${CONFIG_FILE}" &
}

# 主流程开始
cd "${WORK_DIR}"

# 检查v2ray文件是否存在且MD5值是否匹配
if [ ! -f ${V2RAY_BIN} ] || ! echo "${EXPECTED_MD5}  ${V2RAY_BIN}" | md5sum -c -s; then
    download_files
else
    echo "v2ray file exists and MD5 check passed."
fi

# 检查v2ray进程是否已经在运行
if check_v2ray; then
    echo "No v2ray process detected. start v2ray"
    start_v2ray
else
    echo "The v2ray process is either already running."
fi

echo "----------- end -----------"
echo

在写脚本时要注意需要在目标机器上能运行,所以有些命令要稍作调整,比如md5sum的参数略有不同等。目标上也无bash,不要想当然:) 至此,无论啥时,包括重启我们的机器也会自动下载v2ray及配置并且启动,它的代码和程序都是在云上的,那么有点云控的味道了。

折腾没止境了?

事情到这里其实还没有结束,但折腾的时间有限。眼看春节放假(包括休了几天)就要过完了,我木有时间再玩这个了,但也有几点想法待后续有空再折腾。

其一:上面会发现我们的程序二进制过大了,在这小小的存储空间中,你都放不下2个这种程序,为了瘦身,我们可以借助upx等工具将其压缩到较小体积。如以下,32M变8M了,这也太棒了吧!

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
➜  ~ upx --best v2ray
                       Ultimate Packer for eXecutables
                          Copyright (C) 1996 - 2024
UPX 4.2.2       Markus Oberhumer, Laszlo Molnar & John Reiser    Jan 3rd 2024

        File size         Ratio      Format      Name
   --------------------   ------   -----------   -----------
  33095680 ->   8659500   26.17%   linux/mips    v2ray                         

Packed 1 file.

其二:我们在保障主网络稳定下,又想给某些人(如我)有点新特权。我想可以通过iptables DNAT规则在主路由器上做一些配置,让某些设备可以天然默认进入代理模式而不必配置任何东西,即透明代理。初步想了一下,我们可分几步:

  • 在DHCP中预留一些IP资源段,不自动分配出去。
  • 将预留的IP段借助iptables规则,使其在访问时转发给v2ray。这块因时间关系没空玩了,网上似乎也有一点资料,大概也能找到灵感。资料见附录。

其三:上面云控的味道来了!既然我们可以自动下载软件和配置,何不自动下载脚本呢,这样我只有一个壳脚本,通过它来从云端下载资源,未来的想象空间也会更大!你能想到有多少玩法吗?而我,等有时间再来折腾了。

参考资料

-EOF