顯示具有 OpenWrt 標籤的文章。 顯示所有文章
顯示具有 OpenWrt 標籤的文章。 顯示所有文章

2019年11月23日 星期六

OpenWrt REVISION

由 scripts/getver.sh 產生


用途
  • base-files VERSION 一部分
  • VERSION_SED 取代 %R:用在 /etc/banner, /etc/openwrt_version, /etc/openwrt_release, /etc/device_info, /etc/opkg/distfeeds.conf
  • repositories.conf
  • mac80211 編譯 compat_version
  • netgear-dni、tplink-safeloader

2019年10月19日 星期六

[OpenWrt] start over

重生
  • 安全 (failsafe) 模式:只開機到基本可運作狀態,可修改大部分設定和調整套件,修正因為設定錯誤等而造成無法存取的問題。
  • 回復出廠預設值 (factory reset):回復到安裝 OpenWrt 後的原始狀態。
  • 復原 (recovery) 或解救 (rescue) 模式:在損壞的機器安裝新韌體。

安全模式

安全模式只有在 root 分割是唯讀的 squashfs 時支援。可執行
grep squash /proc/mounts
來檢查,回類似
/dev/root /rom squashfs ro,relatime 0 0

開機時,有兩秒的時間可進入安全模式,依機型大致有 3 種進入方式:
  1. LED SYS (或 Power) 閃爍時按 Reset,會變成快閃。
  2. 收到給 192.168.1.255:4919 的封包時按 Reset。
  3. 觀看 serial console 訊息,輸入「f」。
然後可以 ssh 192.168.1.1 (15.05 以前是 telnet 192.168.1.1) 進入或 serial console 下指令。

安全模式指令
mount_root:掛載 jffs2 分割 (出廠後有修改的部份) 到 /overlay。然後可進行修正。

回復出廠預設值

正常開機後或在安全模式都可以回復出廠預設值。使用 squashfs / overlayfs 設定的皆支援,因為回復出廠預設值的作法是 erasing and reformatting the overlayfs。x86 使用 ext4 read-write rootfs 不支援。

參考來源

延伸閱讀

https://wiki.openwrt.org/doc/howto/generic.failsafe
overlay, firstboot, rootfs_data

2019年9月28日 星期六

ubus

ubus (OpenWrt micro bus architecture)

包含 daemon ubusd、函式庫 libubus、和一些額外的 helpers.

ubusd 是 ubus 的心臟,提供界面給其它 daemons 註冊和送訊息,採用 Unix sockets 和 TLV (type-length-value) 訊息。

libubus 簡化使用 ubus 的軟體開發。

每個 daemon 在特定 namespace 註冊一些路徑,每個路徑可提供多個有任意數目引數的 procedure,procedures 可用訊息回應。

http://git.openwrt.org/project/ubus.git

工具指令

指令 ubus 和註冊到 ubus 的服務互動,用來檢查註冊的 namespaces,以及撰寫 shell 指令搞。使用 user-friendly 的 JSON 格式呼叫 procedure 和回應。

用法:ubus [選項] 命令 [參數...]

選項:
-s <socket>:設定要連結的 unix socket,沒指定預設是「/var/run/ubus.sock」
-t <timeout>:設定指令逾時秒數
-S:使用簡化輸出 (指令稿用)
-v:More verbose 輸出
-m <type>:(for monitor): include a specific message type (可多個)
-M <r|t>:(for monitor): only capture received or transmitted traffic

命令:
list [<path>]:列出已註冊的 namespace,-v 的話包含 procedures 和其 argument signatures 也列出。
call <path> <method> [<message>]:呼叫 namespace 的 procedure,可含以 JSON 表示的引數
listen [<path>...]:Listen for events
send <type> [<message>]:送事件通知。
wait_for <object> [<object>...]:等候 multiple objects to appear on ubus
monitor:Monitor ubus traffic

範例
`ubus listen &`:建立一個 listening socket 來觀察新進的事件。
ubus
ubus list
ubus -v list system
ubus call system info

透過 HTTP 存取 ubus

uhttpd 有 ubus plugin -- uhttpd-mod-ubus 讓 ...

在 lua 使用 ubus

Namespaces & Procedures

使用 ubus 常見的專案。

netifd

rpcd

for file, iwinfo, session, uci

Example code snippets

ubusd

ubusd 內部是一個 uloop,監視預設 bind 到「/var/run/ubus.sock」的 fd 所發生的事件,

參考

  1. https://wiki.openwrt.org/doc/techref/ubus
  2. 用ubus實現Inter-Process Communication
  3. ubus_invoke()使用介紹
http://wiki.openwrt.org/doc/techref/netifd

延伸閱讀:http://data.pavlix.net/installfest/2014/openwrt-software.pdf

2019年9月21日 星期六

OpenWrt

OpenWrt 是一個 Linux 發行版本和開發環境。

特點
  • Linux 發行版本 (distribution)
  • 支援許多硬體
  • 跨平台編譯環境
  • 許多套件支援各種功能
  • 許多使用輕量版軟體提供相當的功能 (所以快?)
  • 套件安裝
  • 不含 non-free 驅動程式,以至於有些硬體無法運作。(dd-wrt、Tomato 則有 non-free 的部份)

  • OpenWrt 一開始是在 2004 年基於 Linksys 無線路由器 WRT54G 的 GPL 原始碼。
  • OpenWrt 開發環境基於大量修改過的 Buildroot。
  • 2016 年分出 LEDE,2018 年併回 OpenWrt。
軟體
  • 原始碼:git clone https://git.openwrt.org/openwrt/openwrt.git
  • 開機:preinit
  • uClibc
  • 系統軟體:procd、ubus、netifd
  • 設定管理:uci
  • busybox
  • shell
  • json
  • 套件管理:opkg
  • wget 由 uclinet-fetch 取代
  • telnetd 由無密碼的 dropbear 取代
  • syslog => ubox's logd and logread
  • https://wiki.openwrt.org/doc/techref/netifd 有個表格跟一般 Linux Distribution 及 Android 等比較系統軟體架構
作業系統架構比較
OpenWrtBuildrootDesktop
C 標準函式庫uClibc?glibc
initprocd?systemd
網路設定netifd?NetworkManager
套件管理opkg?apt, ...
原始碼
  • 下載:http, ftp, git, ...。壓縮檔或目錄
  • 不是壓縮檔的話產生壓縮檔:方便帶著走
  • 解壓縮
  • patch
  • configure
  • compile
  • install
參考
  • https://openwrt.org/
  • dd-wrt、Tomato、Gargoyle
  • OpenWrt 開發框架較複雜而不好修改簡化。Buildroot 關聯性較低、比較沒那麼自動化。

2019年3月30日 星期六

hotplug2

hotplug2 是輕巧版的 udev,在 OpenWrt 已經由 procd 取代。

程式 /sbin/hotplug2 會常駐,連結到 netlink socket 讀取 uevent,變數拿來跟每個規則比對,符合的就執行對應的指令。

規則比對有 "==" 及 "!=" 檢查字串是否相等、"~~" 及 "!~" 執行正規表示式比對、"is set" 及 "is unset" 檢查有無此變數。每個規則可以有多項比對用「,」隔開,接著是以 { } 包起來的指令。指令也可以有多個,可執行的指令有:
  • print <text>:印訊息
  • print-event [rule label]:印出整個 event。If the rule label is provided, it is printed along with the rule.
  • setenv <key> <value>:設定環境變數
  • remove <file>:移除檔案
  • chown <owner> <file>:改變檔案的擁有者
  • chgrp <group> <file>:改變檔案的群組
  • chmod <mode> <file>:改變檔案的存取模式
  • run <command>:使用 system() 執行 shell 指令
  • exec <command> [arg1 [arg2 [... [argn]]]]:直接執行指令
  • mknod <filepath> <mode>:建立 device node (使用 MAJOR, MINOR 及 SUBSYSTEM 變數)
  • load-firmware <firmware directory>:載入存在 <firmware directory> 的韌體,需要 FIRMWARE 變數
  • serialize [socket:]:
  • next-event:跳到下個 event,也就是不比對接下來的規則。
  • branch-event [success]:如果上個指令執行失敗 (或成功),跳到下個 event。
  • branch-rule [success]:如果上個指令執行失敗 (或成功),跳出此規則,繼續比對接下來的規則。
最後三個是流程控制。

OpenWrt preinit 時,規則是 /etc/hotplug2-init.rules,如果是按鍵 (SUBSYSTEM == button) 就「kill -USR1 1」。

在 init 的 boot 時,規則會改設為 /etc/hotplug2-init.rules,會再包含 /etc/hotplug2-platform.rules 及 /etc/hotplug2-common.rules。這些規則有一部分會去執行 /sbin/hotplug-call,第一個參數是 SUBSYSTEM 變數值或 firmware,會去執行 /etc/hotplug.d/$1/ 下所有的程式

preinit 時, 如果沒有 devfs,則執行
/sbin/hotplug2 --set-worker /lib/hotplug2/worker_fork.so --set-rules-file /etc/hotplug2-init.rules --no-persistent --set-coldplug-cmd /sbin/udevtrigger
/sbin/hotplug2 --set-worker /lib/hotplug2/worker_fork.so --set-rules-file /etc/hotplug2-init.rules --persistent &
在 init 的 boot
/sbin/hotplug2 --override --persistent --set-rules-file /etc/hotplug2.rules --set-coldplug-cmd /sbin/udevtrigger --max-children 1 >/dev/null 2>&1 &
疑問:
  • hotplug 跟 hotplug2 的關係?
參考來源:
  1. https://wiki.openwrt.org/doc/techref/hotplug
  2. hotplug2 原始碼
  3. OpenWrt 原始碼
延伸閱讀:
  • devtmpfs
  • devfs
  • udev
  • mdev (busybox 的簡化版 udev)
  • hotplug ()

2019年3月24日 星期日

OpenWrt procd

OpenWrt 使用 procd 取代傳統 Linux 使用的 init 及 udev。

procd 原始碼除了套件 procd 外,還包括套件 procd-ujail、procd-seccomp、procd-nand、procd-nand-firstboot。procd 套件包括二進位程式 init、procd、askfirst、udevtrigger,shell 指令檔 reload_config 和 procd.sh,以及 hotplug json 檔 hotplug-preinit.json 和 hotplug.json。


在 OpenWrt 開機,Linux 最後會執行腳本 /etc/preinit註1 開始 userspace 的動作,但現在一開始就先插隊執行 /sbin/init,然後才執行原本的 /etc/preinit,最後交棒給 procd 繼續執行,變成:/sbin/init → /etc/preinit → /sbin/procd。

註: 在 OpenWrt 開機,Linux 最後會執行腳本 /etc/preinit (`try_to_run_init_process("/etc/preinit")`@init/main.c → do_execve()@fs/exec.c), 執行時會檢查檔案開頭決定何種執行檔,腳本需要以 #! 開頭 (load_script()@fs/binfmt_script.c),/etc/preinit 使用的直譯器是 busybox sh

init

一開始 PREINIT 沒設,執行 init 取代自己
[ -z "$PREINIT" ] && exec /sbin/init
  1. `ulog_open(ULOG_KMSG, LOG_DAEMON, "init")`: 設定 log 方式
  2. SIGTERM、SIGUSR1、SIGUSR2 的處置是 sa_shutdown
  3. `early()`:掛載檔案系統及設定環境變數
  4. `cmdline()`:讀取 Linux 指令行參數 init_debug 設定 debug 等級。
  5. `watchdog_init(1)`:初始化 watchdog,使用 /dev/watchdog
  6. fork() 執行 `/sbin/kmodloader /etc/modules-boot.d/` 載入一些 kernel 模組,並等候結束
  7. fork() 執行 `/sbin/procd -h /etc/hotplug-preinit.json` 處理事件
    • 載入韌體:使用 /sbin/hotplug-call
    • failsafe 按鍵:執行 /etc/rc.button/failsafe 產生 /tmp/failsafe_button
  8. fork() 執行 `PREINIT=1 /bin/sh /etc/preinit`,執行 /etc/preinit 原本該跑的腳本,結束時執行
    1. 如果 hotplug-preinit.json 還在跑,殺了他。
    2. 如果檔案 /tmp/sysupgrade 存在,就一直睡吧不往下執行了 (等候 reboot 嗎?) 
    3. 清掉環境變數 INITRAMFS、PREINIT
    4. 設環境變數 WDTFD 交出 watchdog file descriptor
    5. 交接除錯等級
    6. 改為繼續執行 /sbin/procd

procd

procd 其實可看成是兩個程式,有加 -h 參數是當作 hotplug daemon (hotplug_run()),否則
  1. 繼承除錯等級或重設,並設定 log 方式
  2. `setsid()`:建立新 session 並成為 process goup leader,且成為 process group 唯一的成員,使用 process ID 作為 session ID 及 process group ID,回傳 session ID。no controlling terminal。如果已經是 process group leader,則回傳 -1 且 errno 為 EPERM。
  3. `procd_signal()`:signal 的處置
    • SIGTERM、SIGINT、SIGUSR1、SIGUSR2:sa_shutdown
    • SIGSEGV、SIGBUS:sa_crash
    • SIGHUP、SIGKILL、SIGSTOP:sa_dummy
  4. 如果不是第 1 個 process,一秒後連結到 ubus,否則進入以下 state
  5. STATE_EARLY
    1. 設定 watchdog timeout
    2. hotplug(/etc/hotplug.json) 事件處理
    3. fork() 執行 `udevtrigger`,結束 ... 進入下個 state
  6. STATE_UBUS
    1. 重開 /dev/console stdin/stdout/stderr
    2. ubus timeout:ubus 連上後最後進入下個 state
    3. service_start_early()
  7. STATE_INIT
    1. LOG("- init -\n");
    2. `procd_inittab()`:讀取 inittab 建立 actions 列表 (id:run-level:action:process)
      • process 切出 argv
      • 除了 sysinit 及 shutdown 外,可以有多個
    3. `procd_inittab_run("respawn")`:
    4. `procd_inittab_run("askconsole")`
    5. `procd_inittab_run("askfirst")`
    6. `procd_inittab_run("sysinit")`:依序執行 /etc/rc.d/ 下 S 開始的腳本中的 boot() 部份
    7. `ulog_open(ULOG_SYSLOG, LOG_DAEMON, "procd")`:切換到 syslog
  8. STATE_RUNNING:沒做什麼事,只是 log 完成 init
  9. STATE_SHUTDOWN:shutdown 時進入,依序執行 /etc/rc.d/ 下 K 開始的腳本中的 shutdown() 部份
  10. STATE_HALT
sysinit 跟 shutdown 都是執行 /etc/rc.d/ 下的腳本,這些腳本通常是依所要執行的順序連結到 /etc/init.d/ 下的腳本,在 inittab 可能的內容如下:
::sysinit:/etc/init.d/rcS S boot
::shutdown:/etc/init.d/rcS K shutdown
在 procd 會用 glob() 取出所有 /etc/rc.d/S* 的檔案,全部加到 libubox runqueue 去執行。

askfirst

用在 inittab

udevtrigger

...

procd.sh

共用腳本函數,當 init 腳本使用 procd 方式時 (USE_PROCD=1) 使用。

OpenWrt 的 init 腳本通常透過 /etc/rc.common 執行,在開機時是執行 shell function  boot(),而 boot() 預設是執行 start()。在傳統方式,由 init 腳本提供 start() 功能,但當使用 procd 方式時則改提供 start_service()。
oldprocd
start()start_service() 至少要提供 json command 的部份
stop()已採用 procd_kill 停止程式,stop_service()
reload()如有 reload_service() 則執行,不然執行 start()
running()
trace()
使用 procd 方式的特別之處
  • 所有引數會打包成 json 格式,透過 ubus 送給 procd
    • command
    • respawn
    • ...
  • service_triggers() 可用 procd_add_reload_trigger() 登記哪些設定檔改變後執行 reload_config 需要重新執行。可用 procd_add_network_trigger() 登記網路改變作為重啟的 trigger

範例:package/network/services/dnsmasq

詳情見 OpenWrt package/base-files/files/etc/rc.common。
rc.common 會載入 /lib/functions.sh (一些共同的函數,包括 config_ 開頭的函數)、/lib/config/uci.sh (uci_ 開頭存取 uci 的函數)、及 /lib/functions/service.sh (使用 busybox start-stop-daemon 提供 service_ 開頭的函數)。

reload_config

configd 完成前的臨時替代方案,
檢查設定有改變則呼叫 `ubus call service event { "type": "config.change", "data": { "package": "設定檔名" }}`

開機時會產生設定檔的 MD5 checksum 存在記憶體,更改設定後執行 reload_config 會自動重啟相關的程式。

hotplug-preinit.json 及 hotplug.json。

...

延伸閱讀


/sbin/procd (不設 INITRAMFS,不設 PREINIT)
│ hotplug(/etc/hotplug.json)
├→ udevtrigger
│ubus_connect()...service_init, /etc/inittab
│? /sbin/ubus
├respawn→
├askconsole→
├askfirst→/sbin/askfirst
sysinit⇄用 runqueue 一次跑一個 /etc/rc.d/S* boot,pipe STDOUT/STDERR

hotplug_run() 每次事件發生時
exec→
load-firmware→

libubox uloop
libubox blob, blobmsg
json_script_init
ubus
service
ustream

延伸閱讀:http://data.pavlix.net/installfest/2014/openwrt-software.pdf

2015年8月22日 星期六

OpenWrt netifd

netifd (Network Interface Daemon) 用來做網路設定,相當於一般電腦用的 NetworkManager。netifd 取代 OpenWrt 舊的網路設定指令檔,可以管理複雜的網路設定組合,並處理好相依性而不用做太多無謂的動作,其中一項最大的好處就是當 /etc/config/network 改變時,不用所有界面都重啟,只需要執行 /etc/init.d/network reload,就會發出 ubus 呼叫給 netifd,只針對跟目前狀態的不同處設定。

netifd 是一個具有 RPC 能力的 daemon,以便於透過 netlink events 存取 kernel。

/lib/network/*.sh,
/sbin/ifup
some scripts in /etc/hotplug.d

一般電腦用 glib、dbus、udev,而 OpenWrt 使用 libubox、ubus、hotplug2,不需要龐大的 library 及相依,便能提供不錯的功能。

參考來源:
延伸閱讀:
  • procd -- 取代 busybox initd, klogd, syslogd, watchdog
  • ubox -- 取代 block-mount
  • http://data.pavlix.net/installfest/2014/openwrt-software.pdf
     

2014年9月18日 星期四

OpenWrt Network

設定網路界面、routing、Ethernet switch VLAN。

設定檔是 /etc/config/network,執行 /etc/init.d/network reload 重新載入。對特定邏輯網路界面可用 ifup <邏輯網路界面>,如包含有有 Wi-Fi 界面,需多執行 wifi up。

interface 區段:定義邏輯網路界面 (例如 lan、wan 等,可自取名稱),是網路設定的核心,包括 IP 位址、實體界面 (ifname)、alias、route、以及防火牆規則。
  • 設定 IP 位址透過 option proto 及其它相關 option
    • option proto dhcp
    • option proto static
      option ipaddr 192.168.1.1
      option netmask 255.255.0.0
  • option type bridge 將邏輯網路界面加上 bridge 層,可有多個實體界面,例如 option ifname 'eth0 eth1'
  • VLAN 界面是 ifname 加 .<vid>
  • 如果要新增額外的 IP 位址,有兩種方式。舊方式是新增 alisa 區段,透過 option interface 連到邏輯網路界面。新方式是新增另一個 interface 區段,透過相同的 option ifname 並在一起。如要跟 type bridge 的邏輯網路界面併,option ifname 的值前面要加 br-。
  • 有些協定需要搭配 /etc/config/firewall 將兩個邏輯網路界面串在一起,例如 PPtP-over-PPPoE、IPv6-in-IPv4 tunnel。

route/route5 區段:設定 static route,透過 option interface 連到邏輯網路界面。

rule/rule6 區段:透過 option in 連到邏輯網路界面。

switch 區段:設定 Ethernet switch 的 VLAN,跟上述區段比較沒有直接關係。新的 swconfig 框架每個 VLAN 分出 switch_vlan 區段來設定,透過 option device 連到 switch 區段。


switch_port 區段:設定 port VLAN

取得實體網路界面名稱
source /lib/functions/network.sh

if network_get_ipaddr addr "wan"; then
    echo "IP is $addr"
fi

參考來源:
  1. http://wiki.openwrt.org/doc/uci/network
  2. http://wiki.openwrt.org/doc/uci/network/switch
延伸閱讀
  • netifd
  • swconfig
  • /proc/switch

2014年8月16日 星期六

OpenWrt 用到 libpcap 的程式

pcapsipdump: one file per sip session
cshark: CloudShark capture tool
collectd-mod-dns: DNS traffic input plugin
fwknopd:
arpd
wknock
airpwn
daemonlogger
lft
whob
arpwatch
aircrack-ng
dsniff
wiviz
karma
bandwidthd
ulogd-mod-pcap
faifa
rarpd
scdp
scanlogd
snort
snort-mysql
snort-pgsql
snort-wireless
snort-wireless-mysql
snort-wireless-pgsql
pyrit
nmap, nmap-ssl
ncat, ncat-ssl
nping
ucarp
ettercap:用來 injecting traffic
6tunnel
netdiscover:
cdp-tools
tcptraceroute
kismet-drone, kismet-server
scanlogd
raddump
darkstat
ngrep
netdiscover
knock, knockd
softflowd
tstat
wing
ipcad
pmacct-client, nfacctd, pmacctd, sfacctd, uacctdpmacctd-pgsql, nfacctd-mysql, pmacctd-mysql, sfacctd-mysql, uacctd-mysql, nfacctd-pgsql, sfacctd-pgsql, uacctd-pgsql, nfacctd-sqlite, pmacctd-sqlite, sfacctd-sqlite, uacctd-sqlite
nprobe
hping3
reaver
ptunnel
aircrack-ptw
fprobe
ssldump
imsnif
thc-ipv6-*
tcpdump
iftop

參考:libpcap

2014年7月14日 星期一

libubox runqueue

主要是三個函數:runqueue_init()、runqueue_task_add()、runqueue_process_add()

runqueue_init():初始化

runqueue_task_add():將 task 加到 runqueue,並在 1 ms 後嘗試執行,可限制最多同時跑多少 task,也可以限制 task 最久跑多久。嘗試執行時 runqueue 已經空了,沒有 task 待執行或執行中,1 ms 後執行 empty_cb()。

runqueue_process_add():讓 runqueue 知道 task 結束,方便啟動下個 task。

應用參考:OpenWrt /sbin/init

相關:safe_list、uloop
參考來源:libubox

libubox uloop

uloop 是 libubox 的一部份,其功能包括
  • timeout:再多久執行指定函數
  • 執行 child process,結束時執行指定的函數
  • fd 事件發生時執行指定函數。(使用 kqueue 或 epoll)
uloop 使用
  1. 呼叫 uloop_init() 初始化
  2. 註冊 timeout、process、或 fd
    • timeout 透過 uloop_timeout_add() 或 uloop_timeout_set() 註冊,uloop_timeout_cancel() 取消 timeout,uloop_timeout_remaining() 查詢多久 timeout
    • uloop_process_add() 註冊 child process
    • uloop_fd_add() 註冊 fd,uloop_fd_delete() 移除
  3. 呼叫 uloop_run() 執行 uloop 工作迴圈,直到取消。可透過 uloop_end() 取消或用 SIGINT 或 SIGTERM kill
  4. 取消執行後可呼叫 uloop_done() 清掉所有註冊的 timeout 及 child process
應用參考:

2014年7月9日 星期三

OpenWrt boot

OpenWrt 的開機可以分成 bootloader、kernel、preinit、init 階段。

bootloader 階段

  • 硬體初始化及測試
  • 載入 kernel image:Downloading Kernel Image and Initial Ram Disk: 從 flash 或網路載入到記憶體, 需要時作解壓縮。
  • 設定 Linux kernel 需要的開機參數。
  • 跳到 Kernel Entry Point,控制權轉移到 Linux kernel。

kernel 階段

一般 Linux 電腦開機在 kernel 階段最後會去執行 /sbin/init,而 OpenWrt 是去執行 /etc/preinit,進入 preinit 階段。

preinit 階段

/etc/preinit 是 shell script,會載入 /lib/preinit 下更多的 shell script 依序執行,最後掛載根目錄後會去執行 busybox 提供的 /sbin/init,進入 init 階段。

init 階段

/sbin/init 執行哪些程式,由 /etc/inittab 決定。/etc/inittab 有四的欄位,格式是 tty:runlevel;action:command,其中 tty 是 optional,runlevel 沒用到,action 決定執行順序及時機,command 是執行的指令。init 依據 action,依序執行 sysinit → wait → once 的指令,然後進入無窮迴圈執行不斷重新執行 respawn 跟 askfirst 的指令。askfirst 跟 respawn 的差別只是多了等候連接的 tty 按 Enter。但在 NOMMU 處理器無法等候,所以兩者是一樣的。除了上述所提 action 外,還有其它 action:ctrlaltdel (SIGINT 時執行)、shutdown (SIGUSR1, SIGTERM, SIGUSR2 時執行) 跟 restart (SIGQUIT 時執行)。ctrlaltdel 通常設成執行重開機,restart 會重開機,而 shutdown 可能重開機或者關閉電源。

OpenWrt 的 inittab 依處理器或板子不同而有所不同,基本上大概的樣子是
::sysinit:/etc/init.d/rcS S boot
::shutdown:/etc/init.d/rcS K shutdown
ttyS0::askfirst:/bin/ash --login
tty1::askfirst:/bin/ash --login

所以開機時執行 /etc/init.d/rcS S boot,也就是依序執行 /etc/rc.d/ 下,所有 S 開頭的程式裡的 boot() 功能。/etc/rc.d/ 下的程式是連結到 /etc/init.d/ 下的命令檔,這些命令檔大多會帶入 /etc/rc.common,提供預設的動作函數,boot() 預設是執行 start()。也會帶入 /lib/functions.sh (一些共同的函數,包括 config_ 開頭的函數)、/lib/config/uci.sh (uci_ 開頭存取 uci 的函數)、及 /lib/functions/service.sh (使用 busybox start-stop-daemon 提供 service_ 開頭的函數)。

第一個執行的 rc.d 程式通常是 boot,再來可能是 sysctl、ubus、firewall、network

設定

OpenWrt 的設定用 UCI,並提供標準的命令檔函數以便於存取,放在 /etc/functions.sh (也就是 /lib/functions.sh)。

. /etc/functions.sh #載入

config_load config_file # 載入設定檔

如果要特別的回呼,要在 config_load 之前設好 config_cb() 或 option_cb()。config_cb() 每遇到一個新 section 或設定檔載入完畢時呼叫,有兩個參數 -- section type 跟 section name。option_cb() 每個 option 或 list 時呼叫,一樣有兩個參數 -- option name 跟 option value。config_cb() 裡可以修改 option_cb(),讓每個 section 可以有不同的 option_cb()。變數 CONFIG_SECTION 紀錄目前 section 的 ID。

以下對記憶體處理:
  • config_foreach function-called [section-name [function-args ...]]
    • 補充 function-called 的參數
  • config_get variable-stored section-id option-name [default-value]
  • config_set section-id option-name value
  • config_list_foreach section-id  list-name function-called [function-args...]
    • 補充 function-called 的參數
  • config_get_bool:跟 config_get 一樣,多了把 on, true, enabled 取得為 1
參考:http://wiki.openwrt.org/doc/devel/config-scripting

Wi-Fi 啟用

Wi-Fi 預設的設定檔是 /etc/config/wireless。/sbin/wifi 啟用 Wi-Fi,有 detect、up、跟 down 三種動作。在開機 init 階段的 boot 時執行 /sbin/wifi detect,產生設定檔;network 時的 start() 最後執行 /sbin/wifi down 跟 /sbin/wifi up 動作來啟用 Wi-Fi。

/sbin/wifi 整理:
  • 三種動作
    • detect:偵測 Wi-Fi,產生 /etc/config/wireless
    • down:停用 Wi-Fi
    • up (沒指定或其它):啟用 Wi-Fi
  • 載入共用函數 /lib/functions.sh。
  • 載入 /lib/wifi/ 下所有的 .sh:每個 .sh 檔是跟 Wi-Fi 驅動程式搭配,會附加 driver 名稱到 DRIVERS 變數,讓 /sbin/wifi 知道有什麼 driver,並提供 detect_driver()、scan_driver()、disable_driver()、enable_driver() 函數,為完成上述三種動作所需要。
  • 執行 scan_wifi 來載入設定檔。設定檔有一些 type 為 wifi-device 跟 wifi-iface 的 section,一個 wifi-device 可以有多個 wifi-iface。載入過程中,會將 wifi-device 的 section-name 附加到 DEVICES 變數,並把其對應的 wifi-iface 的 section-name 設到 wifi-device 的 vifs 選項,以便後續利用
  • 最後依動作執行對應的函數。
  • 提供的函數
    • find_net_config()
    • bridge_interface()
    • prepare_key_wep()
    • wifi_fix_hwmode()
    • wifi_updown:動作 up 或 down 時執行
      • down → wifi_updown disable→對每個 DEVICES 的 type,也就是 driver 名稱,執行 scan_driver() 跟 disable_driver()
      • up (沒指定或其它)→wifi_updown enable→會先執行 wifi_updown disable,並重新 scan_wifi。如果設定不是 disabled,執行 scan_driver() 跟 enable_driver()
    • wifi_detect:動作 detect 執行的函數, 對每個 DRIVERS 執行 detect_driver(),產生設定檔。最終設定檔會存到 /etc/config/wireless
    • start_net ifname network vifmac:啟用界面,將網路界面 ifname 連到 network 網域。vifmac 作用待研究。
    • set_wifi_up()
    • set_wifi_down
    • scan_wifi
  • 執行 scan_wifi 來載入預設的 Wi-Fi 設定檔
/ib/wifi/*.sh 整理
  • 附加 driver 名稱到 DRIVERS 變數,讓 /sbin/wifi 知道有什麼 driver
  • 提供的函數
    • detect_driver():只有 detect 動作用到,偵測 Wi-Fi 界面,輸出設定檔
    • scan_driver():up 跟 down 動作都會用到,掃描 wifi-iface 的 mode,設定 ifname。wifi-device 設 phy 及重設 vifs
    • enable_driver():
    • disable_driver():
/etc/config/wireless 範例:
config wifi-device radio0 # section name 給 wifi-iface 的 device 選項對應用
        option type     driver # driver 名稱,對應函數 detect_driver(), scan_driver(), enable_driver(), disable_driver()
        option channel  11
        option disabled 1 # 非必要,是否 disabled

config wifi-iface
        option device   radio0 # 對應到 wifi-device
        option mode   ap # 運作模式
        option network  lan # 搭到哪個網域使用
        option ssid     SSID
        option encryption none
完整的 wireless 設定參考 http://wiki.openwrt.org/doc/uci/wireless

參考來源

busybox init/init.c

2014年6月4日 星期三

x86 OpenWrt on USB Drive

x86 OpenWrt 要灌到硬碟很簡單,只需要把產生的 bin/x86/openwrt-x86-generic-combined-ext4.img.gz 解壓縮,然後 dd 到一顆硬碟 [參考]。也可以選擇不設 CONFIG_TARGET_IMAGES_GZIP,編譯時就不會壓縮,產生的 bin/x86/openwrt-x86-generic-combined-ext4.img 可以直接 dd。

用 CF 卡應該也一樣,因為 CF 卡跟 IDE 硬碟界面一樣,不過我沒試過。

USB 隨身碟也是一樣作法,但由於預設產生的 kernel 沒有內建需要的驅動程式,會造成開機無法掛載 USB 隨身碟作為根目錄。只要把需要的驅動程式內建到 kernel 就可以,記得是 USB 相關驅動程式。後來的 OpenWrt 版本就沒有這個問題,沒再細究。

此外還有看到另一種作法,隨身碟用 FAT 檔案系統搭配 syslinux/,但我用 Linux 機器製作後,試的結果並沒成功。

參考:

延伸閱讀:

2014年4月29日 星期二

在 VirtualBox 執行 OpenWrt

準備及啟動
  1. 編譯產生 OpenWrt Image
    git clone git://git.openwrt.org/openwrt.git # trunk,目前是 r40588
    cd openwrt
    make # 可能需要安裝編譯 OpenWrt 所需要的套件,例如 subversion、libncurses5-dev 等,再 make
    # 出現選單,在 Target System 選 x86,在 Target Images 加選 Build VirtualBox image files
    # 經過長久的編譯
    # 產生的 vdi 擋在 bin/x86 目錄下 
  2. 安裝 VirtualBox
  3. 執行 VirtualBox
    • 按「新增」
      • 名稱及作業系統:自訂名稱,類型選 Linux,版本選 Linux 2.6 / 3.x (32 bit)
      • 記憶體大小:至少要約 32MB (試過 16MB 不行)
      • 硬碟:使用現有的虛擬硬碟檔案,選編譯產生的 vdi 檔
      • 建立
    • 按「設定值」
      • 「存放裝置」:可移除「控制器: IDE」
      • 「音效」:可停用
      • 「網路」:我偏好「橋接介面卡」
      • 「序列埠」->「連接埠 1」:勾選「啟用連接埠」
      • 確定
    • 按「啟動」:此時會出現 OpenWrt 的 console 視窗,Enter 後可進行操作。
總共有三個環境:1. 在 openwrt 目錄下的 OpenWrt 編譯環境,2. VirtualBox 管理員,3. VirtualBox 下運行的 OpenWrt 及其 console。

重新編譯

在 OpenWrt 編譯環境下執行「make menuconfig」可選擇所需要的套件,再執行「make」可重新編譯。重新編譯會產生產生新 vdi 檔,在 VirtualBox 下再啟動 OpenWrt 時,會有 UUID 錯誤而無法執行。

解法步驟:
  1. 在 VirtualBox 管理員的「檔案」->「虛擬媒體管理員」->選有使用的 vdi 檔:「釋放」、「移除」
  2. 「設定值」->「存放裝置」->「控制器: SATA」->「加入硬碟」->「選擇現有的磁碟」->選 openwrt 下的 bin/x86 目錄下的 vdi 檔

將 LAN 改為 dhcp

在 OpenWrt console
vi /etc/config/network # 編輯
#修改
ifup lan

編譯為將 LAN 預設 dhcp
到 OpenWrt 編譯環境修改 package/base-files/files/etc/config/network

瘦身

可移除 ppp 等

參考來源:

  1. http://wiki.openwrt.org/doc/howto/virtualbox

2013年11月22日 星期五

Gargoyle HTTP Server

Gargoyle 基於 OpenWrt,除了採用簡易的 WebGUI 設定外,亦提供 QoS, bandwidth monitor 等進階功能。

Gargoyle 有自己的網頁伺服器,改良自 mini_httpd
source reference: httpd_gargoyle.c
主程式呼叫 handle_request()
  1. 從 socket 讀取 HTTP header
    • 每次最多讀 10000 bytes,直到有兩個換行
    • ssl or not
  2. parse request line
    • method, url, protocol
    • method: GET, HEAD, POST
    • url: with '?' for query
  3. parse all other headers,包括 Authorization:、Content-Length:等
  4. url: decode, remove ../, virtual host
    • 檔案不存在處理
    • 目錄:如有 defaultPageFile 或 index 檔,則依照檔案方式處理;不然則 list directory
    • 檔案:不能是禁止取用的檔案,如果符合 CGI 檔格式 (由設定檔 cgipat 參數或指令行 -c 參數設定) 則執行它,不然直接傳送檔案
    • 執行 CGI 檔
      • 必須是 GET 或 POST
      • 建立環境變數 envp
      • 建立執行參數 argp
      • stdin, stdout, stderr 導到 socket,中間可能插入另一個 process
      • execve(CGI 檔, argp, envp)
相關文章:OpenWrt WebUI

2013年11月17日 星期日

OpenWrt WebUI

OpenWrt 的 WebUI 主要有三種:LuCI、X-Wrt Webif、及 Gargoyle。

LuCI
  • Web Server 預設用 uhttpd
  • 使用 Lua 命令檔語言
X-Wrt Webif
  • Web Server 用 busybox httpd
  • shell + awk + haserl 命令檔語言
Gargoyle:基於 OpenWrt 的發行版本,有較易用的 GUI 介面及其它特別的功能,例如:可以對特定 IP 限制頻寬、主動式 QoS

haserl 是一個命令檔語言,本身編譯後約只有 20k,可在 HTML 檔嵌入 shell scripts 作為伺服器端命令檔,產生最終的 HTML 檔案。haserl 程式碼:
  • 本身是執行檔,首行是 #!/usr/bin/haserl
  • 其它內容是 HTML 格式,需要動態產生的部份用 <? ?> 包起來,執行 shell script 產生
參考:
  1. http://wiki.openwrt.org/doc/howto/webinterface.overview
  2. Web Server Comparisons -- 一些網頁伺服器比較,其中 thttpd 似乎不錯。而 gargoyle_httpd 是基於用 fork 的 mini_httpd,效能可能較差。
延伸閱讀:十大易用性原則

2013年10月28日 星期一

OpenWrt feed for Asterisk

asterisk-1.8.x
  • svn://svn.openwrt.org/openwrt/packages (net 目錄) 
    • 33541 複製成 svn://svn.openwrt.org/openwrt/branches/packages_12.09/net/asterisk-1.8.x@33626
      • 34123 更新 lantiq channel driver
    • 34121 最後一版
    • 36485 移除 (36502 移除目錄)
  • http://feeds.openwrt.nanl.de/openwrt/telephony.git
    • 100f27f3 (Fri Jun 21 13:55:47 2013) 跟 34121 的差異
      • 增加套件 asterisk18-pgsql, asterisk18-pbx-lua
      • asterisk18-cdr 及 asterisk18-odbc 的 conffiles 及 install 不同
asterisk-11.x
  • svn://svn.openwrt.org/openwrt/packages (net 目錄) 
    • 35954 最後一版
    • 36485 移除 (36502 移除目錄)
  • http://feeds.openwrt.nanl.de/openwrt/telephony.git
dahdi-linux & dahdi-tools

asterisk-1.4.x
參考:
  1. OpenWrt Feeds (提供一些 feeds 連結)

2013年8月27日 星期二

Ralink Ethernet Driver

OpenWrt 有 Ralink Ethernet Driver

module init
  • debugfs 初始化 (選擇性)
  • esw 初始化
  • 註冊 platform driver:登記 probe 及 remove 函數
platform driver 的 probe 函數
  • get memory base
  • alloc ether net dev
  • get irq
  • 登記 net dev ops:包括 init, uninit, open, stop, start_xmit, tx_timeout, ... 等函數
  • 設定 net dev priv data
  • 註冊 net dev
net dev 的 init 函數
  • ...
  • hardware init
    • request irq
    • alloc 256 rx_info, 256 tx_dma, 256 rx_dma, one skb per rx_info
    • 初始化 tx_dma, 初始化 rx_info dma_addr (來自 skb), 初始化 rx_dma
    • set macaddr to SoC
    • 初始化 SoC tx_dma, rx_dma
    • set sys_freq to SoC
    • tasklet init: tx & rx
    • other SoC init
net dev 的 open 函數
  • ...
net dev 的 start_xmit 函數
  • ...
net dev 的 tx_timeout 函數
  • ...
封包接收
  • ...
硬體加速
  • Hardware NAT, QoS, TCP/UDP/IP checksum offloading
延伸閱讀

2013年8月20日 星期二

OpenWrt 套件定義的 KCONFIG

OpenWrt 套件都要定義
define Package/套件名

define KernelPackage/套件名
裡面的一些參數,例如 TITLE:= 是填顯示的標題名稱。

其中有一項是 KCONFIG:=,預設是空的,可用來增加 Kernel 的設定,例如 package/hostap-driver/Makefile 的 define KernelPackage/hostap,就有設定
KCONFIG:=CONFIG_HOSTAP CONFIG_HOSTAP_FIRMWARE=y CONFIG_HOSTAP_FIRMWARE_NVRAM=y
,如果選了 hostap 套件,Kernel 設定就會增加

CONFIG_HOSTAP=m
CONFIG_HOSTAP_FIRMWARE=y
CONFIG_HOSTAP_FIRMWARE_NVRAM=y
,對應到 Kernel 程式,包括外部模組,就會有

#define CONFIG_HOSTAP_MODULE
#define CONFIG_HOSTAP_FIRMWARE
#define CONFIG_HOSTAP_FIRMWARE_NVRAM

的定義。

套件額外的選單設定 define Package/套件名/config 只影響 OpenWrt 的設定,且不會有對應的程式碼定義。

2013年8月17日 星期六

OpenWrt Unified Configuration Interface

Unified Configuration Interface (UCI) 是 OpenWrt 的設定環境,大部分的設定都使用 UCI,然而還有一些設定並不在 UCI

使用 UCI 的設定檔是文字檔,放在 /etc/config/ 目錄下,可以用文字編輯器編譯,也可以透過 uci 指令存取,或者其它程式 API 存取,例如 shell script 的 /lib/config/uci.sh、C library /lib/libuci.so、lua library /usr/lib/lua/uci.so 等,適合用於資源有限不好執行一個資料庫的環境。

uci 設定檔有三層架構
  1. <config>:/etc/config/ 下的設定檔檔名
  2. <section>、<section-type>:每個設定檔內容分成許多 config 開始的 section,section 有 <section-type>。格式是:
    config <section-type> [<section>]
    <section> 是 section 的名稱,可有可無。沒有 section 名稱時用 @<section-type>[index] 的方式存取。
  3. <option>、<value>:每個 section 有 option 或 list。
uci 設定檔格式
config section-type section-name
config section-type
option option-name option-value
list option-name option-value
# 開始為註解註界
config 表示一個 section 開始,type 是必要的,讓程式決定如何應用。可以沒有 name,稱為 anonymous section。
option 提供選項 name 到 value 的對應,list 單一 name 可以有多個 value。

檔案範例 /etc/config/foo
# cat /etc/config/foo

config bar first
        option name 'Mr. First'
config bar
        option name 'Mr. Second'
config bar third
        option name 'Mr. Third'
        list kid aaa
        list kid bbb
首先這裡存在 <config> 檔 foo,內容分成許多 config 開頭的 section,每個 section 有 option 或 list。範例裡 <section-type> 都是 bar。每個 option 定義一對<option> 及 <value>,而 list 讓一個 <option> 有許多 <value>。

<config>、<section>、<section-type>、<option>、<value>

uci 指令:
# uci
Usage: uci [<options>] <command> [<arguments>]

Commands:
        batch  
        export     [<config>]
        import     [<config>]
        changes    [<config>]
        commit     [<config>]
        add        <config> <section-type>
        add_list   <config>.<section>.<option>=<string>
        show       [<config>[.<section>[.<option>]]]
        get        <config>.<section>[.<option>]
        set        <config>.<section>[.<option>]=<value>
        delete     <config>[.<section[.<option>]]
        rename     <config>.<section>[.<option>]=<name>
        revert     <config>[.<section>[.<option>]]
        reorder    <config>.<section>=<position>

Options:
        -c <path>  set the search path for config files (default: /etc/config)
        -d <str>   set the delimiter for list values in uci show
         -f <file>  use <file> as input instead of stdin
        -L         do not load any plugins
        -m         when importing, merge data into an existing package
        -n         name unnamed sections on export (default)
        -N         don't name unnamed sections
        -p <path>  add a search path for config change files
        -P <path>  add a search path for config change files and use as default
        -q         quiet mode (don't print error messages)
        -s         force strict mode (stop on parser errors, default)
        -S         disable strict mode
        -X         do not use extended syntax on 'show'

透過 <config>.<section>.<option> 存取 <value>,而透過 <config>.<section> 取到的是 <section-type>。<section> 可用特定 <section-type> 的 index 表示法取代,index 從 0 開始,第一個是 @<section-type>[0]。也可以從後面數,此時 index 是負的,從 -1 開始,最後一個是 @<section-type>[-1]。
# uci get foo.first
bar
# uci get foo.first.name
Mr. First
# uci get foo.@bar[0].name
Mr. First
# uci get foo.@bar[1].name
Mr. Second
# uci get foo.@bar[-1].name
Mr. Third
# uci get foo.third.kid
aaa bbb
要注意的是 uci show [<config>] 時,會顯示所有的 <section-type> 及 <value>,<section-type> 的 index 不計有 <section> 的,所以可能會有所不同。
# uci show foo
foo.first=bar
foo.first.name=Mr. First
foo.@bar[0]=bar
foo.@bar[0].name=Mr. Second
foo.third=bar
foo.third.name=Mr. Third
foo.third.kid=aaa bbb


新增
uci add <config> <section-type>
uci set <config>.<section>=<section-type>

touch /etc/config/pbx
uci set pbx.201=user
uci set pbx.201.CFUC=202
uci commit pbx

參考:
  1. http://wiki.openwrt.org/doc/uci
  2. http://wiki.openwrt.org/doc/techref/uci
  3. Using UCI as stand alone (使用範例)
  4.  https://openwrt.org/docs/guide-user/base-system/uci
待續:
  • 設定/新增
    • uci  set <config>.<section>[.<option>]=<value>
    • uci add <config> <section-type>
    • uci add_list <config>.<section>.<option>=<string>
    • uci commit [<config>]
    • uci revert     <config>[.<section>[.<option>]]
  • 其它
    • uci batch
    • uci export [<config>]
    • import     [<config>]
    • changes    [<config>]
    • delete     <config>[.<section[.<option>]]
    • rename     <config>.<section>[.<option>]=<name>
    • reorder    <config>.<section>=<position>

SIP header Via

所有 SIP 訊息 都要有 Via,縮寫 v。一開始的 UAC 和後續途經的每個 proxy 都會疊加一個 Via 放傳送的位址,依序作為回應的路徑。 格式 sent-protocol sent-by [ ;branch= branch ][ ; 參數 ...] s...