最近在折腾OpenWRT,第一次接触,用的是HLK7688的板子

先说个坑,烧了固件之后,WAN口改变,并不是左边独立出来的第一个,我的变成了最右边的那个。所以,如果通过uboot烧固件,怎么都不成功的话,多试试其它网口。硬件部分在图片的基础上略有改动,这个板子有两个串口(就图上的两个),我的板子把串口2卸了,装了个数码管,可以实时统计WIFI设备的连接数量。

好了,我接下来就说说我自己的研究过程心得。

0x01 OpenWrt小试牛刀之环境准备与初次编译

OpenWrt的准备工作我就不详细介绍了,我直接说说我自己的环境吧,还有中途我遇到的一些坑。我会给一些参考链接,不明白的可以参考别人写的比较详细的内容。

1、Linux环境 (Ubunut 16.04)

推荐Ubuntu16.04,我就是使用的Ubuntu16.04,搭配的VMware虚拟机。本来我自己的主机是Kali2016.1_x64,一开始想直接用Kali编译,结果变异过程特别艰难,头文件好多函数名不一样,修改了4、5个头文件,还是编译没出固件,所以不推荐使用Kali,其它的没用过,看了网上的回馈Ubuntu12.04应该也还可以。

2、安装依赖库

apt-get install g++
apt-get install libncurses5-dev
apt-get install zlib1g-dev
apt-get install bison
apt-get install flex
apt-get install unzip
apt-get install autoconf
apt-get install gawk
apt-get install make
apt-get install gettext
apt-get install gcc
apt-get install binutils
apt-get install patch
apt-get install bzip2
apt-get install libz-dev
apt-get install asciidoc
apt-get install subversion
apt-get install libssl-dev

或者

apt-get install g++ libncurses5-dev zlib1g-dev bison flex unzip autoconf gawk make gettext gcc binutils patch bzip2 libz-dev asciidoc subversion libssl-dev

网上很多都少了安装libssl-dev这个库,也可能是Ubuntu16.04的需要手动安装一下,这里需要注意一点。

3、下载OpenWrt项目

我下载的是 Trunk版

自己想要什么版本,可以直接上官方找找,链接: http://git.openwrt.org/

在Linux系统里直接git下来就好了

$ git clone git://git.openwrt.org/openwrt.git

4、更新软件包

进入openwrt项目的目录执行下面两条命令

$ ./scripts/feeds update –a
$ ./scripts/feeds install –a.

5、配置编译配置文件

首先需要生成配置文件,是一个在openwrt根目录下的隐藏文件“.config”

$ make defconfig

这条命令会检查编译环境,如果安装包没安装好,这一步会有提示,然后自行安装就Ok

接下来就可以开始定制自己想要的标准功能了

$ make menuconfig

这条命令是在控制台生成的图形化配置窗口,进入之后就能配置自己的固件了,对应固件的硬件信息,标准软件包,一目了然。熟悉之后,配置完成,保存,会自动在OpenWrt目录配置隐藏的".config"文件。接下来就可以编译了。

6、编译

简单介绍几个常用编译参数以及效果

$ make      // 直接编译,全都使用默认参数
$ make -j8  // -j表示启动多线程编译,8表示8条线程
$ make V=s  // 编译过程打印输出变异过程,如果编译失败,使用这条命令,可以看到出错详细信息,最好和 -j1联用
$ make package/xxx  // 指定编译软件包,最终生成*.ipk,在bin/目录,编译完成拷贝到路由器使用 opkg install *.ipk进行安装

生成的文件,在OpenWrt的bin/目录下,虽然我的板子用的是MT7688,但在实际编译时我配置的目标是MT7628。在目录下会生成很多个*.bin文件,烧录的时候,烧录openwrt-ramips-mt7628-mt7628-squashfs-sysupgrade.bin

第一次接触,这里还有一个点需要注意

在编译完成之后,如果发现没有生成想要的openwrt-ramips-mt7628-mt7628-squashfs-sysupgrade.bin这个文件。

可能是因为生成的包大小超过了,比如说我想直接在路由器里进行开发,选择编译了,编译出来的包有20+M,但我的路由器是根本装不下的,OpenWrt项目会自动判断,就算生成了这个包,你也无法安装,是无效的,就不会生成这个*.bin文件。所以在自定义配置的时候,还是根据需求自己选择必须的功能就好。如果真的就想生成这么大的固件,这个问题我想应该也是可以突破的,不过我还没有研究。

0x02 如何把自己的程序编译进固件

既然是做二次开发,那我们总不能写个程序编译了一个一个路由器里把程序拷贝进去,我们就需要把程序一起编译进固件,烧完路由器,程序也就自然安装好了。

关于如何把自己写的代码编译到固件里其实也很简单。

这里先推荐两篇文章

OpenWRT Makefile框架以及Kernel和firmware生成过程分析
http://www.cnblogs.com/sammei/p/3968916.html

OpenWrt上用C来写一个Helloworld http://scateu.me/2016/12/03/openwrt-helloworld.html

通过上两篇文章,我再把我汲取到的知识简要的分享一下,不懂的可以在博客留言

首先,生成*.ipk包的源文件都是存在于package目录下的

配置.config编译配置文件的时候,会扫描所有目录的makefile文件,那么我们只需要按照OpenWrt给我们的规则来配置makefile文件就行Ok.

我以一个helloword的例子讲述一下这个过程。

1、创建一个规范的目录结构

在package目录下创建一个helloworld文件夹

openwrt/package/helloworld
├── Makefile                    // 暂且称为root_Makefiel
└── src
    ├── helloworld.c
    └── Makefile                // 暂且成为src_Makefile

在root_Makefile写入内容 (这里的root_Makefile表示的是外部的Makefile,注意看上一段的注释)

include $(TOPDIR)/rules.mk

PKG_NAME:=helloworld
PKG_RELEASE:=1

PKG_BUILD_DIR:=$(BUILD_DIR)/$(PKG_NAME)

include $(INCLUDE_DIR)/package.mk

# 这里的宏配置的是在 make menuconfig 中的选项
define Package/helloworld
    SECTION:=utils
    CATEGORY:=Utilities
    TITLE:=Helloworld
endef

define Build/Prepare
    mkdir -p $(PKG_BUILD_DIR)
    $(CP) ./src/* $(PKG_BUILD_DIR)/
endef

define Package/helloworld/install
    $(INSTALL_DIR) $(1)/bin
    $(INSTALL_BIN) $(PKG_BUILD_DIR)/helloworld $(1)/bin/
endef

$(eval $(call BuildPackage,helloworld))

然后在src/helloworld.c里写一个标准教科书版本的 helloword

#include <stdio.h>

int main()
{
    printf("hello world\");
    return 0;   
}

再写一下编译程序的src_Makefile

helloworld: helloworld.o
    $(CC) $(LDFLAGS) helloworld.o -o helloworld
helloworld.o: helloworld.c
    $(CC) $(CFLAGS) -c helloworld.c

clean:
    rm *.o helloworld

OK.这样就可以编译了

启动OpenWrt的编译配置

~$ make menuconfig

我们刚写好的程序,在选项Utilities->helloworld,选中,保存,退出

执行make,就能直接把程序编译到固件里

测试程序也可以先把程序编译成*.ipk包,然后放到路由器里opkg install helloworld.ipk先安装测试一下,再编译固件。

0x03 自启动脚本如何编译进固件

自启动脚本的编译也可以按照上面的思路来进行,最后添加到启动项的时候,只需要在外部Makefile里添加把脚本拷贝到系统目录/etc/init.d/的命令就可以,虽然我小测无果,但我相信这个思路肯定没问题。

我这里说另一种更直接粗暴简单的方法。

将脚本文件放到这个目录下,并且添加可执行权限。

package/base-files/files

这是编译固件的一个基础文件目录,就是说,所有固件中都会包含这个文件夹里的内容。

我就用我的数码管实时显示WIFI用户数量的脚本来说。

我的程序目录结构是这样的

/
├── etc
│    └──count_number
└── usr
    └── share
         └──count_number
                 └──count_number.sh

就只有两个文件

/usr/share/count_number/count_number.sh是我获取WIFI客户连接数量并输出到数码管的脚本实现

/etc/init.d/count_number 是开机启动脚本,实际上的开机启动脚本存在于/etc/rc.d目录下,这个目录我们不用管,他会自动帮我们拷贝/etc/init.d/目录下的文件过去

/etc/init.d/count_number 脚本代码

#!/bin/sh /etc/rc.common
# Copyright (C) 2017 www.wangsansan.com

START=50

start() {
    /usr/share/count_number/count_number.sh &
}

OK,把文件按照目录结构放到package/base-files/文件夹下,并添加可执行权限

这样就完成了。

编译,就直接把脚本编译进固件里了。

0x04 Luci 静态界面修改

好的,固件定制结束了,那就定制一下web页面,先登陆到路由器web页面看看

1、界面底部

PowerBy.png

上图蓝色部分包含超链接、版权支持信息文字和版本信息

看到图,第一个思路,找到Luci的安装目录,再从所有文件中搜索上面那串文字,

如果第一次玩OpenWRT不知道Luci安装目录,可以先找到index.html文件,因为Luci使用的是Lua脚本跑的,所以找到之后,cat查看或者用vim打开index.html,就能找到Luci安装目录。当然,也可以直接在根目录“/”执行查找命令,不过时间会比较漫长。

一般来说Luci的安装目录都是 “/usr/lib/lua/luci”

执行下面的命令。

root@OpenWrt:~# cd /usr/lib/lua/luci
root@OpenWrt:/usr/lib/lua/luci# find ./ -name "*" | xargs grep -Hn "Powered by"

解释一下参数含义,find 命令就不说了

xargs 将stdout输出流测内容传到stdin当作下一条命令的参数

grep

-H //打印文件名

-n //打印行号

返回结果

root@OpenWrt:/usr/lib/lua/luci# find ./ -name "*" | xargs grep -Hn "Powered by"
./view/themes/bootstrap/footer.htm:17:    <a href="https://github.com/openwrt/luci">Powered by <%= ver.luciname %> (<%= ver.luciversion %>)</a> / <%= ver.distversion %>
root@OpenWrt:/usr/lib/lua/luci#

找到那么,vim 打开文件 “./view/themes/bootstrap/footer.htm”

修改第17行,对比原文,分析一下这行html代码什么意思

## 原文 ##
Powered by LuCI Master (git-17.246.24307-3e1ae70) / OpenWrt Designated Driver r49395

## HTML 标签,方便观看,分成三行 ##
<a href="https://github.com/openwrt/luci">
Powered by <%= ver.luciname %> (<%= ver.luciversion %>)
</a> 
/ <%= ver.distversion %>

一眼看过去,嗯,用眼睛翻译一下

<a href="超链接url"> Powered by <变量1> (<变量2>) </a> / <变量3>
变量1 = "LuCI Master"
变量2 = "git-17.246.24307-3e1ae70"
变量3 = "OpenWrt Designated Driver r49395"

且 <变量3> 不包含超链接。

既然知道怎么回事,改起来方法就很多了。

1、可以直接修改变量内容;

2、直接修改此处的html内容;

为了方便,我就直接修改html吧,超链接跳转到我的博客地址,并且居中显示

<a href="https://www.wangsansan.com" style="display:block;text-align:center;">Powered by WangSansan</a>

看看结果

!\[\](img/PowerBywangsansan.png)

好了,底部部分完成。

2、左上角LOGO

LOGO部分,不知道是图片还是纯文字,页面上右键查看源代码,或者直接把页面另存到桌面

查看html,18行

<a class="brand" href="http://192.168.1.1/cgi-bin/luci/#">OpenWrt</a>

可以看到是文字Logo,如果是图片或许还方便一些,直接把logo替换就行,既然是文字,那就找找在哪儿修改

页面底部布局的文件是 “view/themes/bootstrap/footer.htm”

修改完成后退出vim,查看同级目录,包含一个文件 header.htm

vim打开,分析一下。就是这份文件。

162行和184行,修改一下,要加Logo也可以加一个Logo

<title><%=striptags( (boardinfo.hostname or "?") .. ( (node and node.title) and ' - ' .. translate(node.title) or '')) %> - LuCI</title>
...
...
...
<a class="brand" href="#"><%=boardinfo.hostname or "?"%></a>

修改后的代码

<title>WangSansan - LuCI</title>
...
...
...
<a class="brand" href="#">WangSansan</a>

修改后大概这样子

!\[\](img/header.png)

0x05 Web界面功能添加与内容开发

本来想把web功能部分与静态页面修改放到一起写,静态页面的修改,只研究了半个小时就能搞定了,本以为加功能嘛,大概浏览了一下,无非就是一堆Lua代码,增删改也就一会儿的事,研究了才发现除了Lua代码,还有目录结构和包含关系,这部分相对于纯静态页面的修改,还是稍有难度,所以单独分节,好了,接着上面的步骤来说。

静态内容的修改,没什么难度,既然我们是二次开发,那我们就尝试增加点新功能。

先登陆进去

!\[\](img/view.png)

常规的路由器功能,该有的都有。

接下来就可以开始定制属于我们自己的路由器界面了。

不建议直接对原油配置文件的内容进行修改

我们先把LuCi的配置文件是怎么工作的搞明白,在一步一步进行我们的二次开发工作。

LuCI采用了MVC三层架构,使用Lua脚本开发,在/usr/lib/lua/luci目录下,分别对应三个文件夹,做开发的同学,对MVC架构应该就比较熟悉了,模型(model)-视图(view)-控制器(controller),Luci采用的Lua进行开发,我们不需要定义自己的视图框架的话基本不需要对view层进行修改,基本上只需要修改model层就可以完成我们功能的添加。

Luci运行时,会扫描controller目录下的所有*.lua文件,进行功能入口的注册并展示在Web页面

当我们在Web页面点击各功能入口时,Luci会执行model目录下的对应入口功能模型

1、简单的CBI格式

<controller>目录 (注册模块入口)

module("luci.controller.控制器名/路径", package.seeall)
function index()
        entry(url跳转路径, 文件的相对路径, _("显示名称"), 显示顺序)
        end

<model> 目录 (配置CBI Module)

local fs = require "nixio.fs"
local sys = require "luci.sys"

local m, s

m = Map("配置文件文件名", "配置页面标题", "配置页面说明")
s = m:section("创建Section方式", "对象模块名")  
s.anonymous=true

return m

如果读者懂一点Lua语言的话看起来就会方便很多

包含两个变量,分别是m和s

变量m用于映射模块信息,其中Map()上面有函数三个参数

需要注意的是第1个参数:配置文件名,这个配置文件需要放到系统的/etc/config目录下

如果没有创建配置文件的话,访问页面会出错。配置文件可以简单的写一条“config 模块名”,后面会讲。

s = m:section("创建Section方式", "对象模块名")  

这一条是创建模块的,Section分为两种,NamedSection和TypedSection,前者根据配置文件中的Section名,而后者根据配置文件中的Section类型。

2、注册一个模块入口

首先进入controller目录,创建一个文件wangsansan.lua

写入内容

module("luci.controller.wangsansan", package.seeall)

function index()
        entry({"admin", "wangsansan_url"},
                cbi("admin_wangsansan/test"),
                _("Test"))
end

访问一下,可以看到我们已经添加了一个名为“Test”的入口

并且点击之后的url跳转路径是 http://192.168.1.1/cgi-bin/luci/admin/wangsansan_url

entry({"admin", "wangsansan_url"},cbi("admin_wangsansan/test"), _("Test"))

这条语句我折行了,看不懂的话,把它合并到一行来看就行了

第2个参数表示调用的路径,就下面这行内容

cbi("admin_wangsansan/test")

cbi() 这个函数会检索到model目录下,那这一行就是说,目标文件路径为model/cbi/admin_wangsansan/test.lua

![](img/controller_page.png)

不过这个时候我们点击Test会报错,因为我们还没有配置CBI模块

3、配置对应入口的CBI模块

首先创建配置文件

# echo "config wangsansan" > /etc/config/wangsansan

接下来配置CBI模块,创建文件model/cbi/admin_wangsansan/test.lua

写入下面的内容

local fs = require "nixio.fs"
local sys = require "luci.sys"

local m, s

m = Map("wangsansan", "wangsansan_title", "wangsansan_explain")
s = m:section(TypedSection, "wangsansan")
s.anonymous=true

return m

好了,点击Test栏目,访问一下

!\[\](img/cbi_page.png)

好了,可以看到和我们配置的信息是一样的。

自己纯手动建立过一遍,就能弄明白怎么回事,我们可以开始自己定制我们想要的界面了

提供参考连接:

LuCi API
https://htmlpreview.github.io/?https://raw.githubusercontent.com/openwrt/luci/master/documentation/api/index.html

Openwrt Luci界面开发http://blog.csdn.net/lichao_ustc/article/details/42739563

为你的luci添加自助高级配置界面http://www.right.com.cn/forum/thread-183560-1-1.html

Openwrt开发与Luci介绍http://www.jianshu.com/p/bfb93c4e8dc9


折腾几天的成果,可能有些坑爬出来之后,就忘了,如果大家有遇到其它坑,可以在博客下留言,如果恰好我懂,我会回复。

CSDN博客:http://blog.csdn.net/byb123
个人博客:https://www.wangsansan.com
个人微信公众号:iamwangsansan

右侧扫码关注公众号“山中书”