老司机带你玩转小米路由

前段时间,由于家中无人,水管突然爆裂,导致水漫金山,以致于把我珍藏多年的高配 TP-Link 给活生生的淹死了。想当年克俭克勤,好不容易买来准备把玩 Openwrt 的(后来忘了这茬),看来梦想终是破灭了。

于是,在家人好言相劝并不断督促的前提下,我怒下重金在京东买了小米路由3。一个炎热而又百般无聊的下午,京东快递小哥暧昧的给我递上一个包裹,小米路由安静而美丽的躺在里面,久违的清风吹过,家人乐了,而老司机我这次却坐不住了。

这一次,一定要抽空把玩下这货,以免人生再留下遗憾啊!

开启 SSH

一转眼几周过去了,老司机觉得时机已经成熟,可以对它动手了。而这首当其冲的,是需要我们去剥开它的外衣,直入主题。开启 SSH 后,才可以让我们对它一览无遗,而要开启小米路由3的 SSH,首先我们要升级它的 ROM,稳定版是不能开启的。

升级为开发版 ROM

升级很简单,我们进入这个网址:

http://www1.miwifi.com/miwifi_download.html

选择 ROM 标签,然后下载小米路由3的开发版 ROM,注意不要选错路由器哦。我目前下载到的版本是2.13.33,然后进入小米路由器管理界面:http://192.168.31.1 ,进入 [常用设置] -> [系统状态],点击 [手动升级] ,然后选择你下载的 ROM,一段时间的等待后,再进入系统状态查看就已经是你升级的开发版啦。

官方工具开启 SSH

有了开发版这个前提,老司机可以带你用官方工具来开启 SSH 了,进入官方的这个地址:

http://www1.miwifi.com/miwifi_open.html

滚动到 开启SSH工具 这里:

点击进入,这里会显示你 root 的密码,赶快用个小本子记下来吧:

点击下载工具包,然后按照官方的步骤如下:

  1. 请将下载的工具包bin文件复制到U盘(FAT/FAT32格式)的根目录下,保证文件名为miwifi_ssh.bin;
  2. 断开小米路由器的电源,将U盘插入 USB 接口;
  3. 按住 reset 按钮之后重新接入电源,指示灯变为黄色闪烁状态即可松开 reset 键;
  4. 等待3-5秒后安装完成之后,小米路由器会自动重启,之后您就可以尽情折腾啦 :)

不过这里老司机要提醒下,如果你的路由没有固定,那么想一个人完成这整个动作有点困难,对于老司机我,当然就轻车熟路了。

初尝 SSH 连接

一切完毕后,等这一刻是不是已经很久了,那么赶快进入主题吧:

ssh root@192.168.31.1

输入你刚刚记在小本子上的密码,进入后可以看到震撼的欢迎界面:

顿时雷总被鬼畜后那魔性的声音在我脑海中旋转起来,老司机欣然的笑了笑,这工程师文化,牛逼!来不及多想,试验了几个命令,发现opkg都没有了,看来小米对 openwrt 做了些阉割。扫了扫几个 bin 目录,发现工具还是挺多的,包括一个简化版的 vi。然后再扫了扫 lib 目录,发现里面集成的库还是很多的,值得注意的是 C 标准库用的是uClibc

这时候,如果你想在 /root 目录下建立文件,肯定会报只读的错误,没关系,我们重新挂载下 /root,按下面命令执行:

# cd /data
# mkdir -p diy/root
# mount diy/root/ /root

这时候再进入 /root 目录下,就可以进行读写操作了,从现在开始,我们可以做一些更有趣的事情,比如写一个小程序玩玩。

交叉编译环境搭建

小米路由官方提供了插件开发文档,但这种受限且无趣的扩展,老司机是一点都提不起精神,甚至连试都懒得去试。我们需要一些更加激进的玩法,需要更大的掌控权利,所以开发完全原生的应用是个不错的选择。

开发原生应用,那就不得不搭建一个交叉编译环境了,在这之前首先我们要清楚小米路由的 CPU 是什么架构的:

uname -m

输出了mips,呃,mips还有大端mipsbe和小端mipsel区分,这个在没有法写程序的情况下,还真不好区分。没关系,我们先去官方找找看,有没有现成的交叉环境。

进入刚刚下载开启 SSH 工具的地址:http://www1.miwifi.com/miwifi_open.html

选择 小米路由器插件开发文档,下载下来是一个 sdk_package.zip 文件,解压后看到了 toolchain 目录:

什么鬼?怎么这些 toolchain 都是 arm 架构的,完全不符合情理,老司机都不镇定了。用file看了下,这些都是 linux 下的二进制文件,作为有追求的 mac 党,果断删、删、删。

于是,抱着好奇的态度,点了旁边 小米路由Mini插件开发文档,下载下来是一个 sdk_package_r1c.zip 文件,解压后看到了 toolchain 目录:

这下 CPU 架构对了,看来是小端mipsel无疑了,但是老司机怒了,原来这小米路由3就是小米路由mini的升级版啊!没有追求的人可以直接开 linux 虚拟机用这 toolchain 去玩耍了,但老司机是绝对不会做这事。既然官方没有提供 mac 下的 toolchain,老司机决定自己去编译一个。

环境准备

要在 mac 上编译 openwrt,需要有些准备工作,首先用brew安装一些依赖库,逐条执行:

brew tap homebrew/dupes
brew install coreutils findutils gawk gnu-getopt gnu-tar grep wget quilt xz
brew ln gnu-getopt --force

依赖库安装完后,我们需要导一下环境变量:

export PATH="/usr/local/opt/coreutils/libexec/gnubin:$PATH"

其次,mac 的文件系统默认是大小写不明感的(Case-insensitive),而 openwrt 必须在大小写敏感的(Case-sensitive)文件系统上编译,这点也不难,我们创建一个即可:

hdiutil create -size 10g -type SPARSE -fs "Case-sensitive HFS+" -volname OpenWrt OpenWrt.sparseimage
hdiutil attach OpenWrt.sparseimage

创建完毕并附加成功后,我们切入到这个盘中:

cd /Volumes/OpenWrt

接下来就是下载源码了,进入 openwrt 的官方 git 仓库:http://git.openwrt.org ,我们发现最新的稳定版是15.05,那我们就选择这个版本。

git clone git://git.openwrt.org/15.05/openwrt.git

等待克隆完毕,老司机就可以带你编译了!

开始编译

克隆完毕后,我们需要更新和安装下 openwrt 的包列表,执行下面命令:

scripts/feeds update -a
scripts/feeds install -a

等待执行完毕后,开始我们的定制化编译配置了,openwrt 这块做得还是非常人性化的,执行make menuconfig之后,我们看见一个命令行下的图形化配置界面:

这个界面有点酷啊,注意图中老司机的红色箭头标注,这里选择了系统架构和要构建 toolchain,按两次 ESC,保存后,我们就可以开始编译了。

make toolchain/install

这个要耗蛮久的时间,老司机转身就出门抽烟去了,大概 20 多分钟后它才编译完成,这时候新鲜出炉的 toolchain 就在 staging_dir 目录下了:

久违的 Hello World

这一刻我们已经等得太久了,mac 下的 toolchain 在老司机的轻车熟路下很快就构建了出来,那么我们试试写一个经典的Hello World吧!

1
2
3
4
5
6
7
8
#include <iostream>

using namespace std;

int main(void) {
cout << "Hello World" << endl;
return 0;
}

编译前,我们需要指定STAGING_DIR到 openwrt 编译好的 staging_dir,依次执行如下:

export STAGING_DIR=/Volumes/OpenWrt/openwrt/staging_dir
export CXX=/Volumes/OpenWrt/openwrt/staging_dir/toolchain-mipsel_mips32_gcc-4.8-linaro_uClibc-0.9.33.2/bin/mipsel-openwrt-linux-g++
$CXX helloworld.cpp -o helloworld

正确的生成了 helloworld 文件,我们用file命令看一下:

helloworld: ELF 32-bit LSB executable, MIPS, MIPS32 version 1, dynamically linked (uses shared libs), with unknown capability 0xf41 = 0x756e6700, with unknown capability 0x70100 = 0x3040000, not stripped

现在放到小米路由上试试吧:

scp helloworld root@192.168.31.1:/root/

结果如下图:

这个Hello World可真是久违了,那么再来一个更加现代化的Hello World

1
2
3
4
5
6
7
8
9
10
11
12
#include <iostream>
#include <string>

using namespace std;

int main(void) {
[](const std::string &input) {
cout << input << endl;
}("Hello World!");

return 0;
}

使用如下命令编译:

$CXX -std=c++11 helloworld.cpp -o helloworld2

拷贝、执行,完全没问题!你以为这样的 Hello World 就算结束了么?真正的大招现在才开始呢!

放大招,上 Node

Node JS 可以说在跨平台这一块算是做得非常出色的,而在 openwrt 平台上编译 Node JS 也并非难事,最新的非稳定吧 openwrt 中已经包含了 Node 包了,但遗憾的是,最新的 openwrt 使用的是musl-libc实现,这个 C 标准库与uClibc二进制不兼容,所以编译的二进制文件小米路由3是无法使用的。

老司机灵机一动,想到一个最简单的编译方式,把最新版 openwrt 的 Node 包拷贝到15.05中,用15.05来编译,如此一来少了很多麻烦的参数配置。于是乎:

cd /Volumes/OpenWrt

# 克隆最新版到 openwrt_dev
git clone git://git.openwrt.org/openwrt.git openwrt_dev

# 拷贝最新版的 feeds 包
cd /Volumes/OpenWrt/openwrt/package
cp -rf ../../openwrt_dev/package/feeds .

这些都执行完毕后,我们再回到 openwrt 根目录make menuconfig

注意上图中的层次,保存好后,我们直接make -j2,漫长的等待之后,在下面的目录中产生了编译好的 Node:

/Volumes/OpenWrt/openwrt/build_dir/target-mipsel_mips32_uClibc-0.9.33.2/node-v4.4.5/out/Release

我们拷贝到路由上,再次写一个Hello World

1
2
3
4
5
6
7
8
var http = require('http');

http.createServer(function (req, res) {
res.writeHead(200, {'Content-Type' : 'text/plain'});
res.end('Hello World!\n');
}).listen(8080, '0.0.0.0');

console.log('server running at http://0.0.0.0:8080/');

路由器上跑起来,完全没问题:

本地访问起来,也完全没问题:

还能怎么玩

折腾下来,发现 openwrt 的可定制程度还是相当高的,小米路由3嘛,就是一个弱鸡!老司机觉得,接下来可以折腾、折腾小米硬件相关的 API 使用了(libroutermain\libxmrouter)。

当然,永远相信,美好的事情即将发生!