最近在折腾OpenWRT,第一次接触,用的是HLK7688的板子(就是下面这个东西,淘宝找的图)
先说个坑,烧了固件之后,WAN口改变,并不是左边独立出来的第一个,我的变成了最右边的那个。所以,如果通过uboot烧固件,怎么都不成功的话,多试试其它网口。硬件部分在图片的基础上略有改动,这个板子有两个串口(就图上的两个),我的板子把串口2卸了,装了个数码管,可以实时统计WIFI设备的连接数量。
好了,我接下来就说说我自己的研究过程心得。
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文件。所以在自定义配置的时候,还是根据需求自己选择必须的功能就好。如果真的就想生成这么大的固件,这个问题我想应该也是可以突破的,不过我还没有研究。
既然是做二次开发,那我们总不能写个程序编译了一个一个路由器里把程序拷贝进去,我们就需要把程序一起编译进固件,烧完路由器,程序也就自然安装好了。
关于如何把自己写的代码编译到固件里其实也很简单。
这里先推荐两篇文章
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的例子讲述一下这个过程。
在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
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先安装测试一下,再编译固件。
自启动脚本的编译也可以按照上面的思路来进行,最后添加到启动项的时候,只需要在外部Makefile里添加把脚本拷贝到系统目录**/etc/init.d/**的命令就可以,虽然我小测无果,但我相信这个思路肯定没问题。
我这里说另一种更直接粗暴简单的方法。
将脚本文件放到这个目录下,并且添加可执行权限。
package/base-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/**文件夹下,并添加可执行权限
这样就完成了。
编译,就直接把脚本编译进固件里了。
好的,固件定制结束了,那就定制一下web页面,先登陆到路由器web页面看看
上图蓝色部分包含超链接、版权支持信息文字和版本信息
看到图,第一个思路,找到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: Powered by <%= ver.luciname %> (<%= ver.luciversion %>) / <%= 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 标签,方便观看,分成三行 ##
Powered by <%= ver.luciname %> (<%= ver.luciversion %>)
/ <%= ver.distversion %>
一眼看过去,嗯,用眼睛翻译一下
Powered by <变量1> (<变量2>) / <变量3>
变量1 = "LuCI Master"
变量2 = "git-17.246.24307-3e1ae70"
变量3 = "OpenWrt Designated Driver r49395"
且 <变量3> 不包含超链接。
既然知道怎么回事,改起来方法就很多了。
1、可以直接修改变量内容;
2、直接修改此处的html内容;
为了方便,我就直接修改html吧,超链接跳转到我的博客地址,并且居中显示
Powered by WangSansan
看看结果
好了,底部部分完成。
LOGO部分,不知道是图片还是纯文字,页面上右键查看源代码,或者直接把页面另存到桌面
查看html,18行
OpenWrt
可以看到是文字Logo,如果是图片或许还方便一些,直接把logo替换就行,既然是文字,那就找找在哪儿修改
页面底部布局的文件是 “view/themes/bootstrap/footer.htm”
修改完成后退出vim,查看同级目录,包含一个文件 header.htm
vim打开,分析一下。就是这份文件。
162行和184行,修改一下,要加Logo也可以加一个Logo
<%=striptags( (boardinfo.hostname or "?") .. ( (node and node.title) and ' - ' .. translate(node.title) or '')) %> - LuCI
...
...
...
<%=boardinfo.hostname or "?"%>
修改后的代码
WangSansan - LuCI
...
...
...
WangSansan
修改后大概这样子
本来想把web功能部分与静态页面修改放到一起写,静态页面的修改,只研究了半个小时就能搞定了,本以为加功能嘛,大概浏览了一下,无非就是一堆Lua代码,增删改也就一会儿的事,研究了才发现除了Lua代码,还有目录结构和包含关系,这部分相对于纯静态页面的修改,还是稍有难度,所以单独分节,好了,接着上面的步骤来说。
静态内容的修改,没什么难度,既然我们是二次开发,那我们就尝试增加点新功能。
先登陆进去
常规的路由器功能,该有的都有。
接下来就可以开始定制属于我们自己的路由器界面了。
不建议直接对原油配置文件的内容进行修改
我们先把LuCi的配置文件是怎么工作的搞明白,在一步一步进行我们的二次开发工作。
LuCI采用了MVC模式,使用Lua脚本开发,在**/usr/lib/lua/luci**目录下,分别对应、、三个文件夹,做开发的同学,对MVC架构应该就比较熟悉了,模型(model)-视图(view)-控制器(controller),Luci采用的Lua进行开发,我们不需要定义自己的视图框架的话基本不需要对view层进行修改,基本上只需要修改model层就可以完成我们功能的添加。
Luci运行时,会扫描controller目录下的所有*.lua文件,进行功能入口的注册并展示在Web页面
当我们在Web页面点击各功能入口时,Luci会执行model目录下的对应入口功能模型
module("luci.controller.控制器名/路径", package.seeall)
function index()
entry(url跳转路径, 文件的相对路径, _("显示名称"), 显示顺序)
end
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类型。
首先进入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
不过这个时候我们点击Test会报错,因为我们还没有配置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栏目,访问一下
好了,可以看到和我们配置的信息是一样的。
自己纯手动建立过一遍,就能弄明白怎么回事,我们可以开始自己定制我们想要的界面了
提供参考连接:
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
欢迎关注公众号