OpenWrt Buildroot & Image Builder: Optimiertes Firmware Image und eigene Pakete

Ein wichtiger Grund für das Erstellen angepasster und eigener Firmware Images ist der in vielen Geräeten mit 4MB knapp bemessene Flash Speicher. Die aktuellen Snapshots (trunk genannt) sind minimal. Sie haben keine Weboberfläche installiert. Eine Nachinstallation benötigt viel mehr von dem knappen Speicher, da die Dateien dort im Gegensatz zur Firmware, nicht komprimiert gespeichert werden.

Bei Geräten mit einem freien USB-Anschluss kann mit einem kleinen USB-Stick ein grösseres (root-)Dateisystem "darüber gelegt" werden. Wie man das macht, steht hier beschrieben: OpenWrt: Alternative Routersoftware

Für die digitalen Spiegelreflexkameras von Nikon und Canon gibt es eine leistungsstarke und preisgünstige Lösung für die Fernsteuerung per Smartphone oder Tablet: DslrDashboard auf TL-MR30. In diesem Artikel wird der dafür notwendige 'ddserver' als Paket in die Firmware integriert. Im verlinkten sind die fertigen Firmware Images zum Herunterladen angehängt.

Verwendete Hard- und Software

  • Entwicklungsplattform: Fedora 19 (64bit) auf einem älteren einfachen Laptop, mit automake, make, usw. (Mit minimalen Linux- und Shellkenntnissen empfiehlt sich auf Windows, bspw. mit VirtualBox eine virtuelle Maschine einzurichten und auf diese Fedora-Live zu installeren, die Entwicklerpakete werden via Internet nachinstalliert).
  • WLan-Router: TP-Link TL-MR3040: Ein portabler WLan-Router mit Ethernet, USB und 4MB Flash-Speicher und Akku für den mobilen Betrieb.
  • Ein Switch (LAN), Netzwerkkabel

In diesem Artikel werden zwei Methoden kurz anhand eines konkreten Beispieles beschrieben. Die erste und einfachere beschreibt, wie man aus den vorkompilierten Quellen eine angepasste Firmware erstellt. Die zweite Methode, die aufwändiger ist, kompiliert aus den Quellen den Linux Kernel und die Pakete selber. Diese wird benötigt, wenn externe Pakete für die aktuelle Firmware kompiliert und erstellt werden sollen. Anders ausgedrückt: Image Builder (erstellt aus fertigen Paketen das Firmware Image, OpenWrt buildroot erstellt die Firmware aus den Quelldateien.

Ein eigenes Image zusammenbauen mit Image Builder

Referenz: http://wiki.openwrt.org/doc/howto/obtain.firmware.generate
Quellen: http://downloads.openwrt.org/snapshots/trunk/ar71xx/

Im Heimverzeichnis wird ein Verzeichnis openwrt erstellt, in das das aktuelle ImageBuilder-Archiv herunter geladen wird. Als User, nicht als root (bei 'w get' muss das Leerzeichen entfernt werden wird ausgeführt:

cd ~; mkdir openwrt; cd openwrt
w get http://downloads.openwrt.org/snapshots/trunk/ar71xx/OpenWrt-ImageBuilder-ar71xx_generic-for-linux-x86_64.tar.bz2
tar xvf OpenWrt-ImageBuilder-ar71xx_generic-for-linux-x86_64.tar.bz2
cd OpenWrt-ImageBuilder-ar71xx_generic-for-linux-x86_64/

Trunk-Version (ohne Weboberfläche)

Ein minimales Image, wie die Trunk-Version, erstellt man mit:

make image PROFILE=TLMR3040

Das ergibt:

ls -laR bin
bin/ar71xx:
total 29776
drwxrwxr-x. 2 jh jh    4096 21. Mär 12:55 .
drwxrwxr-x. 3 jh jh    4096 21. Mär 12:51 ..
-rw-rw-r--. 1 jh jh    1012 21. Mär 12:55 md5sums
-rw-rw-r--. 1 jh jh 1966080 21. Mär 12:55 openwrt-ar71xx-generic-root.squashfs
-rw-r--r--. 1 jh jh 1835012 21. Mär 12:55 openwrt-ar71xx-generic-root.squashfs-64k
-rw-rw-r--. 1 jh jh 3932160 21. Mär 12:55 openwrt-ar71xx-generic-tl-mr3040-v1-squashfs-factory.bin
-rw-rw-r--. 1 jh jh 2949124 21. Mär 12:55 openwrt-ar71xx-generic-tl-mr3040-v1-squashfs-sysupgrade.bin
-rw-rw-r--. 1 jh jh 3932160 21. Mär 12:55 openwrt-ar71xx-generic-tl-mr3040-v2-squashfs-factory.bin
-rw-rw-r--. 1 jh jh 2949124 21. Mär 12:55 openwrt-ar71xx-generic-tl-mr3040-v2-squashfs-sysupgrade.bin
-rw-rw-r--. 1 jh jh 1490733 21. Mär 12:55 openwrt-ar71xx-generic-uImage-gzip.bin
-rw-rw-r--. 1 jh jh 1079009 21. Mär 12:55 openwrt-ar71xx-generic-uImage-lzma.bin
-rwxr-xr-x. 1 jh jh 3243348 21. Mär 12:55 openwrt-ar71xx-generic-vmlinux.bin
-rwxr-xr-x. 1 jh jh 3309852 21. Mär 12:55 openwrt-ar71xx-generic-vmlinux.elf
-rw-rw-r--. 1 jh jh 1507328 21. Mär 12:55 openwrt-ar71xx-generic-vmlinux.gz
-rw-rw-r--. 1 jh jh 1114112 21. Mär 12:55 openwrt-ar71xx-generic-vmlinux.lzma
-rwxr-xr-x. 1 jh jh 1150973 21. Mär 12:55 openwrt-ar71xx-generic-vmlinux-lzma.elf

Das Image …sysupgrade.bin ist dasjenige, das man verwendet, wenn OpenWrt bereits auf dem Router installiert ist.

Zum Vergleich das vorher herunter geladene trunk-Image. (Ein cmp ist nicht möglich.) Die beiden Versionen sind aufs Byte genau gleich gross.

-rw-r--r--. 1 jh jh 3932160 21. Mär 00:41 openwrt-ar71xx-generic-tl-mr3040-v2-squashfs-factory.bin

Diese Pakete sind in der trunk Version installiert:

echo $(opkg list_installed | awk '{ print $1 }')

base-files busybox dnsmasq dropbear firewall fstools hostapd-common ip6tables
iptables iw iwinfo jshn kernel kmod-ath kmod-ath9k kmod-ath9k-common kmod-cfg80211
kmod-crypto-aes kmod-crypto-arc4 kmod-crypto-core kmod-gpio-button-hotplug
kmod-ip6tables kmod-ipt-conntrack kmod-ipt-core kmod-ipt-nat kmod-ipt-nathelper
kmod-ipv6 kmod-leds-gpio kmod-ledtrig-default-on kmod-ledtrig-netdev kmod-ledtrig-timer
kmod-ledtrig-usbdev kmod-lib-crc-ccitt kmod-mac80211 kmod-nls-base kmod-ppp kmod-pppoe
kmod-pppox kmod-slhc kmod-usb-core kmod-usb-ohci kmod-usb2 libblobmsg-json libc libgcc
libip4tc libip6tc libiwinfo libjson-c libjson-script libnl-tiny libubox libubus libuci
libxtables mtd netifd odhcp6c odhcpd opkg ppp ppp-mod-pppoe procd swconfig uboot-envtools
ubox ubus ubusd uci wpad-mini

Trunk Version ohne PPP, IPv6 aber mit Unterstützung für USB-Sticks

make image PROFILE=TLMR3040 PACKAGES='kmod-usb-storage kmod-scsi-core kmod-fs-vfat \
 kmod-nls-iso8859-1 kmod-nls-iso8859-15 kmod-nls-cp437 kmod-nls-cp850 block-mount \
-ppp -ppp-mod-pppoe -kmod-ppp -kmod-pppoe -kmod-pppox -ppp \
-odhcp6c -odhcpd -kmod-ipv6 -ip6tables'

Das ist ungefähr der maximalen Speicherplatz, den man überhaupt einsparen kann:

df -h /
Filesystem                Size      Used Available Use% Mounted on
rootfs                    1.2M    220.0K    996.0K  18% /

Trunk Version mit Weboberfläche Luci, mit Unterstützung für USB-Sticks

Luci nachträglich installiert in der Minimalversion benötigt 670K Platz auf dem root-fs

df -h /
Filesystem                Size      Used Available Use% Mounted on
rootfs                    1.2M    888.0K    328.0K  73% /

Für die "Einbettung" im squash-fs, dem Flash-Image braucht es diese Pakete für eine vollständige Luci Installation:

luci-lib-sys liblua libuci-lua lua libubus-lua luci-lib-core luci-lib-nixio luci-sgi-cgi luci-lib-web
luci-proto-core luci-i18n-english luci-mod-admin-core libiwinfo-lua luci-theme-base
luci-theme-bootstrap luci-app-firewall luci-lib-ipkg luci-proto-ppp luci-mod-admin-full
uhttpd uhttpd-mod-ubus luci

Das ergibt folgenden Befehl:

make image PROFILE=TLMR3040 PACKAGES='luci-lib-sys liblua libuci-lua lua libubus-lua luci-lib-core \
luci-lib-nixio luci-sgi-cgi luci-lib-web luci-proto-core luci-i18n-english luci-mod-admin-core libiwinfo-lua \
luci-theme-base luci-theme-bootstrap luci-theme-openwrt luci-app-firewall luci-lib-ipkg luci-proto-ppp \
luci-mod-admin-full uhttpd uhttpd-mod-ubus luci'

Nun ist luci im Image, installiert wird nun … sysupgrade.bin.

Tipp: Am einfachsten geht das, in dem man in der trunk Version luci installiert und dann via Webinterface das Update macht. Die Einstellungen bleiben erhalten.

Trunk Version mit Weboberfläche Luci, mit Unterstützung für USB-Sticks, den Hardware-Schalter, ohne PPP, ohne IPv6

PPP brauchen wir nicht, das spart einige Dutzend KB.

make image PROFILE=TLMR3040 PACKAGES='luci-lib-sys liblua libuci-lua lua \
libubus-lua luci-lib-core luci-lib-nixio luci-sgi-cgi luci-lib-web \
luci-proto-core luci-i18n-english luci-mod-admin-core libiwinfo-lua \
luci-theme-base luci-theme-bootstrap luci-theme-openwrt luci-app-firewall \
luci-lib-ipkg luci-proto-ppp luci-mod-admin-full uhttpd uhttpd-mod-ubus luci \
kmod-usb-storage kmod-scsi-core kmod-fs-vfat block-mount \
kmod-nls-iso8859-1 kmod-nls-cp437 kmod-nls-cp850 \
-ppp -ppp-mod-pppoe -kmod-ppp -kmod-pppoe -kmod-pppox -ppp'

Mit PPP (das man nur dann braucht, wenn man selber die Verbindung mit dem ISP herstellen will, manchmal für einzelne recht nützlich, aber hier nicht):

df -h /
Filesystem                Size      Used Available Use% Mounted on
rootfs                  576.0K    220.0K    356.0K  38% /

Ohne PPP, ein rechter Gewinn:

df -h /
Filesystem                Size      Used Available Use% Mounted on
rootfs                  704.0K    220.0K    484.0K  31% /

Brauchen wir IPv6? Nein, es geht noch ohne. ;-] Nun sind schon über 600K frei. Diese Pakete mit Minuszeichen versehen in obiger Befehlszeile: -odhcp6c -odhcpd -kmod-ipv6 -ip6tables

df -h /
Filesystem                Size      Used Available Use% Mounted on
rootfs                  832.0K    224.0K    608.0K  27% /

Den Hardware-Schalter aktiviert man mit dem kernel-Modul: kmod-button-hotplug
Den Schalter kann man so benutzen: https://forum.openwrt.org/viewtopic.php?pid=189097#p189097 Ein angepassetes Script folgt hier irgendwann.

Ergebnis

Die aktuelle Trunk-Version (ohne Weboberfläche) liess 816KB frei. Installiert man Luci und die USB-Pakete nach, verbleiben einem nur noch unter 100KB. Die angepasste Firmware lässt 608KB frei. Genug Platz für die Installation weiterer Pakete.

Ein eigenes Image bauen mit Build Root

Referenz: http://wiki.openwrt.org/doc/howto/buildroot.exigence und http://wiki.openwrt.org/doc/devel/packages

Um ddserver (Kamera Fernsteuerung) als Paket für die aktuelle Trunk-Version zu erstellen, muss build-root installiert werden. Auf dem lokalen PC installiert man als root:

yum install git-core gcc-c++ ncurses zlib ncurses-devel zlib-devel subversion

Vollständig für Fedora 19 aus obigem Link ('w get' unten wie immer hier zusammenfügen):

yum install -y subversion binutils bzip2 gcc gcc-c++ gawk gettext flex \
 ncurses-devel zlib-devel make patch unzip perl-ExtUtils-MakeMaker \
 glibc glibc-devel glibc-static quilt ncurses-lib sed sdcc intltool \
 sharutils bison w get

Dann folgt man der Anleitung http://wiki.openwrt.org/doc/howto/buildroot.exigence 1:1. Darum hier nicht mehr dazu.

Crosscompilation des ddserver

(Das Verzeichnis ist package und nicht packages, wie im obigen Link angegeben)

Dieses Kapitel zeigt in Kürze, wie man ein Paket aus seine Quellen generiert. Im Prinzip wird hier zugleich die aktuelle trunk-Version erstellt. Aber eben nur diese. Es fehlen schlussendlich nicht nur einige nicht aktivierte Pakete und Kernelmodule, sondern alle Zusatzpakete. Darum verschmelzen wir die beiden Ansätze, die zu einem Flash-Image führen. 'Image Builder' liefert die vorkompilierten Pakete und fertigt die Flash-Images. 'Root Builder' stellt die Infrastruktur zum eigenen Erstellen von Paketen aus dem Quelltext dar. Mehr dazu unten. 

Man holt sich die Quellen mit git.

git clone git://github.com/hubaiz/DslrDashboardServer package/DslrDashboardServer
make menuconfig

Unter Multimedia DDServer anwählen, als Modul (M), nicht in den Kernel integrieren (*).

make

Das 'make' dauert seine Zeit, erst müssen alle Tools herunter geladen werden. Man könnte das Paket auch direkt kompilieren, jedoch müssen dann noch einige Einstellungeng gemacht werden. Darum gleich alles. (Man könnte sich wie beim Image Builder eine massgeschneiderte Image Firmware erstellen lassen). Nach langer Zeit, einigen Stunden auf dem etwas älteren Laptop, befindet sich das Paket dann in:

./openwrt/bin/ar71xx/packages/ddserver_0.2-11_ar71xx.ipk

und kann nun wie gewohnt installiert werden.

Immer noch auf dem lokalen PC kopiert man das Packet mittels scp auf den Router:

cd ./openwrt/bin/ar71xx/packages/
scp ddserver_0.2-11_ar71xx.ipk root@192.168.0.230:/tmp

Darauf hin einloggen im Router (ssh root@192.168.0.230 )

cd /tmp
opkg update && opkg install ddserver_0.2-11_ar71xx.ipk
df -h /
Filesystem                Size      Used Available Use% Mounted on
rootfs                  832.0K    580.0K    252.0K  70% /

Die Abhängigkeiten werden aufgelöst: libusb libpthread librt libstdcpp. Die letzteren Pakete kann man gleich beim Build Image angeben, libusb wird nachinstalliert.

df -h /
Filesystem                Size      Used Available Use% Mounted on
rootfs                  640.0K    256.0K    384.0K  40% /

Update des Snapshots

Die Quellen müssen nicht bei jedem Update der trunk-Version neu übertragen werden. Man wechselt ins Repository (Depot) und zieht sich die Updates. Für diesen Artikel gilt:

cd ~/openwrt/openwrt
git pull
(git merge origin)

Symbiose aus OpenWrt Buildroot und Image Builder: Das Paket für das schnelle Image Building benutzten

Ein einfache Lösung, um schnell an fertige Firmware Images zu kommen, ist die "Symbiose" dieser beiden Methoden. Dazu wird das fertige Paket aus dem Buildroot-Verzeichnis in das richtige Verzeichnis des Image Builders kopiert. Dann fährt man wie oben fort. Auf dem lokalen PC:

cd ~/openwrt
cp ./openwrt/bin/ar71xx/packages/ddserver_0.2-11_ar71xx.ipk \
 OpenWrt-ImageBuilder-ar71xx_generic-for-linux-x86_64/packages/

Und schlussendlich ddserver bereits im squash-fs integriert. Zusätzliche Pakete im ddserver image zum erweiterten trunk image: hotplug2, kmod-usb-ohci, libusb-1.0, openssh-keygen, openssh-server (werden wohl nicht benötigt), uclibc, zlib (werden mit installiert).

Auf dem lokalen PC:

cd OpenWrt-ImageBuilder-ar71xx_generic-for-linux-x86_64
make image PROFILE=TLMR3040 PACKAGES='luci-lib-sys liblua libuci-lua lua libubus-lua \
luci-lib-core luci-lib-nixio luci-sgi-cgi luci-lib-web luci-proto-core luci-i18n-english \
luci-mod-admin-core libiwinfo-lua luci-theme-base luci-theme-openwrt luci-app-firewall \
luci-lib-ipkg luci-mod-admin-full uhttpd uhttpd-mod-ubus luci kmod-usb-storage \
kmod-scsi-core kmod-fs-vfat kmod-nls-iso8859-1 kmod-nls-cp437 kmod-nls-cp850 \
block-mount -ppp -ppp-mod-pppoe -kmod-ppp -kmod-pppoe -kmod-pppox -ppp \
-odhcp6c -odhcpd -kmod-ipv6 -ip6tables libpthread librt libstdcpp ddserver'

Das Image installieren. Nun zeigt sich:

df -h /
Filesystem                Size      Used Available Use% Mounted on
rootfs                  640.0K    224.0K    416.0K  35% /

Beispiele für Firmware-Images

TL-WDR3600 TL-WDR4300 TL-WD4310

make image PROFILE=TLWDR4300 PACKAGES='luci-lib-sys liblua libuci-lua lua \
libubus-lua luci-lib-core luci-lib-nixio luci-sgi-cgi luci-lib-web \
luci-proto-core luci-i18n-english luci-mod-admin-core libiwinfo-lua \
luci-theme-base luci-theme-bootstrap luci-theme-openwrt luci-app-firewall \
luci-lib-ipkg luci-proto-ppp luci-mod-admin-full uhttpd uhttpd-mod-ubus luci \
kmod-usb-storage kmod-scsi-core kmod-fs-vfat block-mount kmod-fs-ext4 \
kmod-nls-iso8859-1 kmod-nls-cp437 kmod-nls-cp850 kmod-nls-iso8859-15 \
luci-app-qos luci-i18n-german luci-app-firewall luci-app-upnp luci-app-hd-idle \
e2fsprogs kmod-usb-storage-extras kmod-usb-ohci e2fsprogs'

TL-MR4030

make image PROFILE=TLMR3040 PACKAGES='luci-lib-sys liblua libuci-lua lua libubus-lua \
luci-lib-core luci-lib-nixio luci-sgi-cgi luci-lib-web luci-proto-core luci-i18n-english \
luci-mod-admin-core libiwinfo-lua luci-theme-base luci-theme-openwrt luci-app-firewall \
luci-lib-ipkg luci-mod-admin-full uhttpd uhttpd-mod-ubus luci kmod-usb-storage \
kmod-scsi-core kmod-fs-vfat kmod-nls-iso8859-1 kmod-nls-cp437 kmod-nls-cp850 \
block-mount -ppp -ppp-mod-pppoe -kmod-ppp -kmod-pppoe -kmod-pppox -ppp \
-odhcp6c -odhcpd -kmod-ipv6 -ip6tables libpthread librt libstdcpp ddserver kmod-button-hotplug'

TL-WR841

make image PROFILE=TLWR841 PACKAGES='luci-lib-sys liblua libuci-lua lua \
libubus-lua luci-lib-core luci-lib-nixio luci-sgi-cgi luci-lib-web \
luci-proto-core luci-i18n-english luci-mod-admin-core libiwinfo-lua \
luci-theme-base luci-theme-bootstrap luci-theme-openwrt luci-app-firewall \
luci-lib-ipkg luci-proto-ppp luci-mod-admin-full uhttpd uhttpd-mod-ubus luci \
luci-app-qos luci-i18n-german luci-app-firewall'