![]() |
Opennet Firmware
|
Die Opennet-Firmware basiert auf den Komponenten git, quilt und der OpenWrt-Entwicklungsumgebung. Links zur Dokumentation dieser Komponenten findest du am Ende dieses Dokuments.
Die grundlegende Struktur der Entwicklungsumgebung ist in der [Struktur-Dokumentation] (Struktur.md) beschrieben.
Die Einrichtung der Entwicklungsumgebung dauert nur wenige Minuten und ist untenstehend beschrieben. Der gesamte Prozess der Image-Erzeugung erfordert dagegen ca. 10 GB Festplattenplatz je Zielarchitektur und dauert - je nach Rechner - üblicherweise mindestens ein dutzend Stunden.
Debian / Ubuntu:
apt install build-essential git flex gcc-multilib doxygen file gawk unzip python3 quilt \ libncurses5-dev zlib1g-dev liblzo2-dev libssl-dev rsync qemu-utils \ python3-distutils python3-lib2to3
Quellcode Zugriff:
git clone https://github.com/opennet-initiative/firmware.git
Die lokale Arbeitsumgebung wird mit folgenden Kommandos abgeschlossen:
cd firmware make init
Die obige Aktion wird eine Weile dauern, da die OpenWrt Repositories heruntergeladen werden.
Für die komfortable Verwendung des Patch-Verwaltungsystems quilt sollten ein paar quilt-Einstellungen gesetzt werden.
Als dauerhafte Lösung ist folgendes möglich - dabei werden jedoch eventuell vorhandene quilt-Einstellungen gelöscht:
cp opennet/quiltrc ~/.quiltrc
Alternativ können die Einstellungen auch zu Beginn jeder Shell-Sitzung importiert werden:
source opennet/quiltrc
Falls die obigen Einstellungen nicht gesetzt werden, wird quilt unnötige Patch-Korrekturen vornehmen, sobald sich Zeitstempel ändern.
Dies ist nicht wünschenswert.
Auf einem Opennet Server ist ein automatischer Build-Dienst konfiguriert, der kurz nach einem push einen neuen Snapshot der Firmware erzeugt. Innerhalb von ca. 90 Minuten sind die ersten neuen Images dann verfügbar: http://downloads.opennet-initiative.de/openwrt/testing
Nach dem Auschecken editiere die gewünschten Dateien. Wenn du .patch-Dateien editieren willst, musst du weitere Dinge beachten (siehe andere Abschnitte).
Um deine Änderungen einzuchecken, führe folgende Kommandos aus:
# lokal einchecken git commit # (Versuch) Änderungen remote einzuchecken git push # Wenn es keinen Fehler gibt, bist du fertig. Glückwunsch!
Nun kann es sein, dass andere Personen zwischendurch Änderungen gemacht haben. Wenn dem so ist, bekommst du eine Fehlermeldung mit entsprechendem Hinweis. Dieses Problem kannst du folgendermaßen lösen:
# alle vorher eingespielten Patches zurückspielen make unpatch # hole alle Änderungen von remote und wende deine Änderungen darauf an git pull --rebase # deine Änderungen remote einchecken git push
Prinzipiell ist die Arbeit mit dem opennet-Repository identisch mit dem üblichen Umgang mit git-Arbeitsumgebungen. Lediglich die quilt-Patches führen unter besonderen Bedingungen zu einem leicht geänderten Verhalten. Daher wird anstelle des üblichen git pull
folgende Abfolge empfohlen:
# lokale Patches zurücknehmen (vermeidet Konflikte, falls Patch-Dateien geändert werden) make unpatch # entfernte Änderungen git pull --rebase
Achtung: git pull --rebase
manipuliert deine lokale git-History. Falls du frische lokale Commits also bereits zu einem anderen Server übertragen haben solltest, dann führt dies zu unüberschaubaren Chaos. Verzichte in diesem Fall auf --rebase
.
Im OpenWrt Repository befindet sich das Basissystem und OpenWrt-spezifischer Code. Externe Pakete befinden sich (in Form von Makefiles, die auf upstream-Quellen verweisen) in den separaten Paket-Repositories.
# alle Patches anwenden (falls der neue Patch am Ende angehängt werden soll) quilt push -a # einen neuen Patch beginnen quilt new IRGENDEIN_THEMA.patch # Hinweis: "quilt edit" ist identisch zu "quilt add", manuellen Änderungen und einem anschließenden "quilt refresh" quilt edit openwrt/IRGENDEINE_DATEI # Patch beschreiben quilt header -e # ausprobieren ... # falls weitere Änderungen vorgenommen wurden, dann nochmal die Patch-Datei aktualisieren quilt refresh # Patch committen git commit patches/IRGENDEIN_THEMA.patch
Im Gegensatz zum OpenWrt Repository befinden sich in den Paket-Repositories lediglich Makefiles (Rezepte für den Paketbau) und OpenWrt-spezifische Patches. Unsere Änderungen sind üblicherweise ebenfalls Patches, die zu den OpenWrt Patches hinzukommen.
quilt new PATCH_NAME.patch
quilt add packages/net/openssh/patches/042-FOO.patch
https://openwrt.org/docs/guide-developer/toolchain/use-patches-with-buildsystem
quilt refresh
quilt header -e
git commit patches/PATCH_NAME.patch
quilt pop -a
quilt push PATCH
quilt edit DATEI
quilt refresh
quilt header -e
Die verwendeten Basis-Repositories (OpenWrt), Pakete, Routing und Luci sind als git-submodules in das opennet-Firmware-Repository eingebunden. Somit sind sie auf spezifische Commits (mittels ihrer Hash-ID) festgelegt. Neue Commits in dem zugrundeliegenden Branch werden von uns also nicht automatisch verwendet. Daher sollten wir gelegentlich zum aktuellen HEAD des Basis-Branch (z.B. "openwrt-19.07") wechseln.
make pull-submodules # die geänderten Submodule-Commit-IDs für zukünftige Builds festlegen git commit -m "Update upstream sources" luci openwrt packages routing
Ziel: den Patch patches/oni-feeds.patch verändern (z.B. um einen weiteren Feed zu erweitern)
# alle Patches zurücknehmen make unpatch # alle Patches auflisten quilt series # alles bis zu dem gewünschten Patch anwenden quilt push oni-feeds.patch # die neue Feeds-Quelle eintrage vi openwrt/feeds.conf # Patch-Datei aktualisieren quilt refresh # den neuen Patch in das Repository hochladen git commit patches/oni-feeds.patch -m "andere feeds-Dinge hinzugefügt"
Für die schnelle Lösung von Build-Problemen ist es oft sinnvoll, nur das eine problematische Paket erstellen zu lassen:
make -C openwrt package/on-core/{clean,compile} V=s
Die Liste vorhandener Pakete und ihrer Einstellungen wird mit dem feeds-Skript von OpenWrt verwaltet. Die Feeds werden mittels des meta-Makefile vor jedem Build und vor jedem Aufruf von make menuconfig
aktualisiert. Du kannst dies jedoch auch manuell auslösen:
make feeds
Eine detaillierte Fehlerausgabe erhältst mit der make-Zugabe von V=s
:
make ath79 V=s
Dabei erleichtert es den Überblick deutlich, wenn du parallele Build-Prozess (z.B. -j 3
) nicht verwendest. Andernfalls musst du eventuell ein paar Seiten in der Build-Ausgabe zurückblättern, um die Fehlermeldung zu finden.
Wie üblich in make-Buildumgebung kannst du manuell mehrere parallele Prozesse für den Paketbau verwenden. Als Faustregel wird üblicherweise ein Wert von Anzahl der Kerne + 1 empfohlen. Bei einem vier-Kern-Rechner wäre dies folgende Zeile:
make -j 5
In den ersten 20 Zeilen der Build-Ausgabe wirst du ein paar Fehlermeldung bezüglich -j1
finden - diese sind ein Indikator für eine OpenWrt-spezifische Unfeinheit. Der finale Build-Prozess wird ungeachtet dieser Warnungen parallelisiert ablaufen.
Die opennet-Entwicklungsumgebung verwendet grundlegend die Abläufe der OpenWrt Buildumgebung. Es gibt jedoch ein paar Besonderheiten, bzw. Komfortfunktionen.
Da die opennet-Firmware verschiedene Ziel-Plattformen (ath79, ixp44, x86, ...) unterstützt, müssen verschiedene Konfigurationen gepflegt werden. Zur Erleichterung der Pflege und zur Vermeidung von Doppelungen gibt es für jede Ziel-Plattform eine separate Datei (z.B. opennet/config/ath79), sowie eine Datei mit Einstellungen, die für alle Zielplattformen gelten (opennet/config/common). Letztere ist für den Entwicklungsprozess wohl die wichtigere.
Die plattform-spezifische config-Datei wird durch opennet/config/Makefile mit der allgemeinen config-Datei zusammengefügt. Anschließend werden folgende Ersetzungen vorgenommen:
__PKG_STATUS__
wird durch stable oder snapshots ersetzt (je nachdem, ob der aktuelle git-commit ein Versions-Tag trägt)Die finale OpenWrt Konfiguration wird aus der Ziel-Plattform-Konfiguration und der allgemeinen Konfiguration erstellt und anschließend mittels make -C openwrt defconfig
durch OpenWrt auf Abhängigkeiten geprüfen und mit den Standardwerten aufgefüllt.
Diese Konfiguration für eine Plattform kann beispielsweise mittels make config-arx71xx
erstellt werden. Das Ergebnis ist anschließend als openwrt/.config verfügbar.
Zur verbesserten Überschaubarkeit von Einstellungsänderungen ist in dem meta-Makefile ein Target namens diff-menuconfig
integriert. Es zeigt dir nach dem Ausführen des gewohnten make -C openwrt menuconfig
den Unterschied zwischen der vorherigen und der gespeicherten Konfiguration in Form eines diffs an.
Unter /usr/lib/opennet/
liegen mehrere Dateien, die Shell-Funktionen beinhalten. Alle Funktionen werden durch die folgende Zeile im globalen Namensraum verfügbar gemacht:
. "${IPKG_INSTROOT:-}/usr/lib/opennet/on-helper.sh"
Alle Funktionen aus den Shell-Bibliotheken lassen sich folgendermaßen prüfen:
on-function get_zone_log_interfaces on_mesh
Dabei ist zu debug-Zwecken auch die ausführliche Ausführungsprotokollierung verfügbar:
ON_DEBUG=1 on-function get_zone_log_interfaces on_mesh
Die optionale strikte Fehlerbehandlung durch die Shell erleichtert das Debugging. Sie lässt sich global in der Datei /usr/lib/opennet/on-helper.sh
mit folgender Zeile aktivieren:
set -eu
In allen Skripten sollten Fehler-Traps aktiviert werden, um den Ort der Entstehung von Problemem leichter zu ermitteln. Folgende Zeile ist im Kopf von nicht-trivialen Funktionen einzutragen, wobei der Funktionsname zu ersetzen ist:
trap "error_trap NAME_DER_FUNKTION $*" EXIT
Leider ist es in der ash-Shell nicht möglich, reine Fehler (siehe set -e
) abzufangen (siehe echo 'trap "echo klappt" ERR' ash
). Daher müssen wir die in der ash-Shell vorhandene allgemeinere EXIT
-trap verwenden. Dies führt leider dazu, dass wir gewünschte Fehler (z.B.: return 1
in einer Wahrheitswert-Funktion) von ungewünschten Fehlern (durch set -e
abgefangen) explizit unterscheiden müssen. In jeder Funktion, die explizit ein false
-Ergebnis zurückliefern möchte (und die traps verwendet), muss folgende Zeile anstelle von return 1
(bzw. anderen Fehlercodes) eingesetzt werden:
trap "" EXIT && return 1
Im System-Log (logread
) lassen sich ausgelöste traps finden:
logread | grep trapped
Folgende Stolperfallen sind sehr beliebt bei der Verwendung des strikten Fehlermodus:
[ 1 -lt "$x" ] && echo "foo"
als letzte Zeile einer Schleife zu einem Abbruch, da die Schleife mit dieser (nicht-Abbruch-auslösenden, jedoch gleichzeitig nicht-erfolgreichen Kette) nach ihrem letzten Durchlauf als "nicht erfolgreich" gilt und somit zu einem Abbruch führtreturn 0
oder true
oder ein angehängtes | true
kann nie schadenDa der typische uci-Aufruf uci -q get CONFIG_KEY
bei nicht-existenten Schlüsseln einen Fehlercode zurückliefert, müsste hier jeder Aufruf von unübersichtlichem boilerplate-Code umgeben werden, um mit strikter Shell-Fehlerbehandlung zu funktionieren. Alternativ steht diese Funktion zur Verfügung:
uci_get CONFIG_KEY [DEFAULT_VALUE]
Das Ergebnis ist ein leerer String, falls der config-Wert nicht vorhanden oder leer ist. Andernfalls wird der Inhalt zurückgeliefert.
Außerdem ist eine verbesserte Version von uci add_list
verfügbar, die - im Unterschied zum Original - bereits vorhandene Einträge nicht erneut hinzufügt:
uci_add_list CONFIG_KEY=NEW_VALUE
Die Funktion uci_delete
entspricht ihrem Äquivalent (uci delete
) bis auf die fehlende Fehlermeldung und der Fehlercode im Falle der Löschung eines nicht vorhandenen Knotens:
uci_delete CONFIG_KEY
Die Opennet Versionsnummern entsprechen dem Commit Zähler. Ab und zu ist es nötig zu wissen, welcher konkrete Commit zu welcher Versionsnummer gehört. Hier gibt es eine einfache Kommandozeile
git log --oneline | sed -n '1!G;h;$p' | nl
Dies listet alle Commit mit entsprechender ONI Versionsnummer auf und einem Abstrakt der Commit Nachricht.
Das OpenWrt Webinterface basiert auf lua-Skripten. Der grundlegende Code sollte in den Shell-Bibliotheken untergebracht werden - lediglich die für die Darstellung notwendige Logik gehört in die lua-Skripte.
Ein paar wenige Funktionen werden von mehreren lua-Skripten verwendet. Diese liegen derzeit in der Datei on-core/files/usr/lib/lua/luci/model/opennet/funcs.lua
.
Die wichtigsten Funktionen sind folgende:
Hinzu kommen ein paar Funktionen für die Erleichterung alltäglicher Dinge:
Zum Debuggen von Fehlern im Web-Interface sind folgende Kommandos sinnvoll:
killall -9 uhttpd 2>/dev/null; sleep 1; rm -rf /var/luci-*; uhttpd -h /www -p 80 -f
Denselben Effekt erreichst du mit der Funktion run_httpd_debug (blockierend - inkl. Fehler-Ausgaben des Webservers), sowie clean_luci_restart (Webserver neustarten und im Hintergrund ausführen):
on-function run_httpd_debug # Debug on-function clean_luci_restart # Daemon
OpenWrt verwendet procd
für die Behandlung von hotplug-Ereignissen. Skripte liegen unter /etc/hotplug.d/
. Für unsere netzwerkbasierten Ereignisse (z.B. Hinzufügen neuer olsrd-Interfaces) verwenden wir den hotplug-Typ iface
.
Der Aufruf von hotplug-Skripten lässt sich folgendermaßen emulieren:
ACTION=ifup INTERFACE=on_mesh hotplug-call iface
Das Skript opennet/tools/check_for_obsolete_functions.sh
gibt potentiell unbenutzte lua- und Shell-Funktionen aus. Ein gelegentliches Prüfen der Ausgabe dieses Skripts hilft dabei, nicht mehr benötigte Funktionen zu beräumen.
Das Skript opennet/tools/copy-package-files-to-AP.sh
kopiert die Inhalte des lokalen Entwicklungsverzeichnisse direkt auf einen angebenen AP. Dies ist möglich, da in unseren Pakete lediglich Interpreter-Code (shell/lua) vorhanden ist und somit kein Build-Prozess erforderlich ist.
Falls auf dem Ziel-AP rsync installiert ist, wird die Übertragung deutlich beschleunigt.
opennet/tools/copy-package-files-to-AP.sh 172.16.0.1
Derzeit existieren lediglich Code-Stil-Prüfungen. Funktionale Tests sind nicht vorhanden.
Mehr Log-Ausgaben (debug) ins syslog (logread) schreiben:
uci set on-core.settings.debug=true
Ein Skript mit detaillierter Ausführungsverfolgung starten:
ON_DEBUG=1 on-function print_services
Fehlermeldungen des Web-Interface ausgeben:
killall -9 uhttpd 2>/dev/null; sleep 1; rm -rf /var/luci-\*; uhttpd -h /www -p 80 -f
Für Performance-Optimierungen sind Daten zum Zeitbedarf der verschiedenen Funktionen sehr hilfreich.
Mit dem folgenden Kommando werden alle Shell-Skripte der opennet-Pakete derart manipuliert, dass fortan der Zeitbedarf jedes Funktionsaufrufs aufgezeichnet wird:
on-function enable_profiling
Vor der Manipulation der Shell-Skripte prüft das obige Skript, ob die notwendigen Zusatzpakete (z.B. bash) intalliert sind.
Die obige Aktion ist irreversibel. Die einzige Möglichkeit, das Profiling wieder abzuschalten, ist die erneute Installation der Pakete.
Die Ergebnisse des Profiling werden unter /var/run/on-profiling
abgelegt. Für jede Funktion wird eine Datei erzeugt. Jede Zeile in diesen Dateien entspricht dabei einem Funktionsdurchlauf. Die gemessene Zeit entspricht dem zeitlichen Abstand von Funktionseintritt und -ende. Die Verarbeitungszeit der aufgerufenen Funktionen geht also in den Zeitbedarf der sie aufrufenden Funktion ein.
Die Auswertung des Profiling ist folgendermaßen möglich:
on-function summary_profiling
Mit fortlaufendem Profiling wird das tmp-Verzeichnis (bzw. der RAM) schrittweise gefüllt. Ein Host mit aktiviertem Profiling darf also nicht im produktiven Einsatz sein, da im Verlauf von Stunden eine Speichermangel-Situation droht.
Die verschiedenen Opennet-Pakete helfen bei der Modularisierung. Einige Pakete ermöglichen eine gewisse Nutzer-Funktion (z.B. on-openvpn: der Tunnel für den Internetzugang). Andere Pakete stellen Infrastruktur bereit, die von anderen Pakete benötigt wird (z.B. on-certificates).
Einige Pakete können über das Web-Interface installiert werden. Diese Pakete müssen an folgenden Orten aufgeführt werden:
Das minimalste Paket ist on-goodies - es besteht lediglich aus einer Liste von Abhängigkeiten und ist daher gut als Vorlage geeignet. Zusätzlich sollten die meisten Pakete Initialisierungs- und Aufräumaktionen in postinst- und prerm-Skripten unterbringen.
Die Übersetzungen werden mittels des luci-Übersetzungskonzepts verwaltet. In den Templates verwenden wir englische Originaltexte.
<%:This is an example.%>
luci.i18n.translate("Interface")
luci.i18n.translatef("Send an email to %s for further information.", email_address)
make translate
virtaal opennet/po/de/on-core.po
Die Versionsnummer des kommenden Release ist in der opennet/config/common
als CONFIG_VERSION_NUMBER
eingetragen.
Im Laufe der Erzeugung der config-Datei wird eventuell die git-commit-Nummer hinzugefügt (siehe Zusammensetzung einer config-Datei).
Die opennet-relevanten Pakete (on-core u.s.w) erhalten dieselbe Versionsnummer.
Wir verwenden buildbot als Web-Interface und Bauumgebung: https://dev.opennet-initiative.de/.
Die buildbot Software wird durch einen git-commit ausgelöst und regt wenige Minuten nach einem git push einen Build-Prozess auf dem zugeordneten Bau-Server an. Die Build-Schritte sind im Web-Interface sichtbar.
Innerhalb des Build-Prozess wird das export-Skript ausgeführt. Es kopiert das Build-Ergebnis für eine Plattform oder wahlweise die erstellte Dokumentation in das Export-Verzeichnis, welches via Webserver veröffentlicht wird (http://downloads.opennet-initiative.de/openwrt/). Das exakte Zielverzeichnis ergibt sich dabei aus der Versionsnummer (siehe Versionsnummerierung).
Bei RAM-Mangel (erkennbar am spontanen reboot ohne Änderungen nach dem Upload der neuen Firmware-Datei) kann folgende Kommandozeile wahrscheinlich genügend Platz schaffen:
for a in collectd dnsmasq sysntpd cron; do /etc/init.d/$a stop; done
Alterantiv koennen diese Dienste via Administration -> System -> Systemstart
gestoppt (nicht deaktiviert!) werden.
Für ein Release sind folgende Schritte durchzuführen:
git tag -a v0.5.1
git push origin v0.5.1
cd /var/www/downloads/openwrt && mv ../../downloads-buildbot/export/VERSION stable/VERSIONONLYNUMBER
latest
korrigieren (siehe ls -l /var/www/downloads.opennet-initiative.de/openwrt/stable
)opennet/config/common
erhöhenopennet/changes.txt
entfernenroles/virtualization-server/templates/vhost-admin.sh
im ansible-RepositoryDie git-Doku befindet sich hier: http://git-scm.com/documentation
Wir verwenden das Patch-Verwaltungssystem quilt. Dies erleichtert die Erstellung und Pflege von Patch-Serien gegenüber fremden Quellen.
Das Howto von quilt ist hier zu finden: http://repo.or.cz/w/guilt.git/blob/HEAD:/Documentation/HOWTO.md
Die Entwicklungsdokumentation von OpenWrt ist hier zu finden: https://openwrt.org/docs/guide-developer/start
Die OpenWrt Build Umgebung ist in https://openwrt.org/docs/guide-developer/build-system/use-buildsystem beschrieben.