最近在折騰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/
這是編譯固件的一個基礎文件目錄,就是說,所有固件中都會包含這個文件夾裏的內容。
我就用我的數碼管實時顯示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、界面底部
上圖藍色部分包含超鏈接、版權支持信息文字和版本信息
看到圖,第一個思路,找到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>
看看結果
好了,底部部分完成。
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>
修改後大概這樣子
0x05 Web界面功能添加與內容開發
本來想把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目錄下的對應入口功能模型
1、簡單的CBI格式
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
不過這個時候我們點擊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欄目,訪問一下
好了,可以看到和我們配置的信息是一樣的。
自己純手動建立過一遍,就能弄明白怎麼回事,我們可以開始自己定製我們想要的界面了
提供參考連接:
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
歡迎關注公衆號