Einführung
Ich arbeite als Programmierer in der Entwicklungs- und Testabteilung von Sicherheitstools für mobile Plattformen des Unternehmens Security Code. Das mobile Entwicklungsteam wurde beauftragt, die plattformübergreifende Continent-AP-Bibliothek der Teilnehmerstation zu portieren, die bereits erfolgreich auf IOS und Android betrieben wurde. Das Hauptproblem war, dass das Sailfish-Betriebssystem nicht so gut dokumentiert ist wie Android oder IOS, aber dank der Leute von Open Mobile Platforms, die die Dokumentation geteilt haben.
VPN-API-Architektur in Sailfish OS
Für alle Netzwerkverbindungen im Sailfish-Betriebssystem ist der ConnMan-Dienst verantwortlich, nämlich:
- Scannen Sie Wi-Fi-Netzwerke und Mobilfunknetze und stellen Sie eine Verbindung zu ihnen her.
- Sharing-Verbindung (Wi-Fi-Hotspot);
- Übersetzung, Ausgangsverbindungen zum Flugmodus;
- VPN-Verbindungsverwaltung.
Ich werde ausführlicher auf den letzten Punkt eingehen. ConnMan verfügt über verschiedene Arten vordefinierter VPN-Plugins. QML-Widgets zum Erstellen und Konfigurieren von Verbindungen sind in die Firmware integriert.
Abb. 1 Sailfish OS-Systemmenü zum Konfigurieren und Verwalten von VPN-VerbindungenDie Benutzeroberfläche unseres VPN-Clients ist ein RPM-Paket und wird während der Installation nicht in das VPN-Systemfenster im Abschnitt Einstellungen integriert, sondern sieht aus wie eine separate Anwendung. Es wird wahrscheinlich einen separaten Artikel über die Entwicklung der Benutzeroberfläche geben, daher wird die folgende Geschichte über die Entwicklung des ConnMan-Plug-Ins in C / C ++ handeln.
Abb. 2 Continent-AP GUI von SailfishVPN-api Sailfish OS besteht aus den folgenden Komponenten, die wir am Beispiel unseres VPN-Clients zeigen werden:
- ConnMan ist der Prozess, der beim Starten des Sailfish-Betriebssystems gestartet wird.
- connman-vpnd ist ein von ConnMan gestarteter Daemon-Prozess, mit dem VPN-Verbindungen verschiedener Anbieter verwaltet, die Tun-Schnittstelle initialisiert und de-initialisiert und über DBus empfangene Netzwerkeinstellungen (IP-Adressen, Routen, DNS-Server) zugewiesen werden. In unserem Fall ein Anbieter namens Kontinent.
- Das Kontinent-proto-plugin.so VPN-Plugin ist eine Bibliothek mit einer Makrodeklaration zum Laden in die Laufzeit und Funktionen, die beim Aufrufen der Schnittstellenmethoden net.connman.vpn.Connection aufgerufen werden.
- Die Hintergrundanwendung (Binärdatei / usr / sbin / kontinent) ist ein Konsolenclient zum Herstellen einer Verbindung mit der CD (Continent Access Server), von der Netzwerkeinstellungen empfangen werden, die an connman-vpnd übergeben werden.
- ConnMan Task ist ein Prozess zum Starten, Stoppen und Überwachen eines laufenden Konsolenclients.
- DBus-api - repräsentiert connman-vpnd, nämlich net.connman.vpn mit den Schnittstellen net.connman.vpn.Manager, net.connman.vpn.Connection.
Abb. 3 Interaktion von Komponenten untereinander in "Continent-AP" SailfishVPN-Plugin
Alle Plugins von Drittanbietern, die nicht Teil der ConnMan-Distribution sind, stellen die Bibliothek dar und sollten bei der Installation über den Paketmanager in / usr / lib / connman / plugins-vpn / abgelegt werden.
Ein Plugin kann eine Konfigurationsdatei haben, in der wir angeben, von welchem Benutzer die Binärdatei ausgeführt werden soll, und deren Rechte vorschreiben. Die Datei sollte sich im System entlang des Pfads /etc/connman/vpn-plugin/continent.conf befinden, und ihr Name sollte dem Namen unseres Anbieters entsprechen, in unserem Fall kontinent.conf.
Dateiinhalt zum Beispiel:
[VPN Binary] User = nemo Group = vpn SupplementaryGroups = inet,net_admin
Das Plugin kontinent-proto-plugin.so in ConnMan wird mit dem Makro CONNMAN_PLUGIN_DEFINE (Name, Beschreibung, Version, Init, Exit) registriert. In unserem Beispiel sieht der Makroaufruf folgendermaßen aus:
CONNMAN_PLUGIN_DEFINE(continent, "continent VPN plugin", CONNMAN_VERSION, CONNMAN_PLUGIN_PRIORITY_DEFAULT, continent_init, continent_exit);
Das Argument name (Kontinent) muss ohne Anführungszeichen stehen. Die Funktionen continent_init, kontinent_exit werden aufgerufen, wenn das Plugin geladen und entladen wird, z. B. wenn systemctl restart connman während der RPM-Installation aufgerufen wird. Die Funktionontin_init ruft die Funktionen vpn_register und connman_dbus_get_connection auf.
vpn_register(name, driver, binary_path)
name - der Name des registrierten Anbieters, in unserem Fall "Kontinent";
Treiber - Struktur vpn_driver Struktur, die Zeiger auf Rückruffunktionen enthält, z. B. beim Zugriff auf das Plugin über DBus;
binary_path - Pfad zur Binärdatei, in unserem Fall "/ usr / sbin / kontinent".
Mit der Funktion connman_dbus_get_connection können Sie die hergestellte DBus-Verbindung DBusConnection * herstellen.
Die Funktion construct_exit ist erforderlich, um die Registrierung des Plugins in ConnMan aufzuheben und die DBus-Verbindung zu schließen.
Eine VPN-Provider-Instanz wird erstellt, wenn DBus die Methode net.connman.vpn.Manager.Create aufruft. Eine entsprechende Einstellungsdatei wird automatisch im Verzeichnis /var/lib/connman/provider_${Hostasket_{VPN.Domain} erstellt. Der Anbieter wird durch Aufrufen von net.connman.vpn.Manager.Remove gelöscht. Wenn die Methode net.connman.vpn.Connection.Connect aufgerufen wird, werden die Einstellungen in den erstellten Anbieter struct vpn_provider * geladen.
Ich möchte auch über einige Funktionen in struct vpn_driver sprechen, von denen einige für die Implementierung erforderlich sind.
connect - Rückruf, der aufgerufen wird, wenn net.connman.vpn.Connection.Connect mit der entsprechenden Adresse des DBus-Objekts über DBus aufgerufen wird, hat die Signatur:
static int continent_connect( struct vpn_provider *provider, struct connman_task *task, const char *if_name, vpn_provider_connect_cb_t cb, const char *dbus_sender, void *user_data)
Das zweite Argument für diesen Rückruf ist eine struct connman_task * -Task. Sie führt die Binärdatei aus, aber Sie müssen Argumente übergeben, bevor Sie beispielsweise den Serverhost und den Port starten:
connman_task_add_argument(task, "--host", value); connman_task_add_argument(task, "--port", value);
Wir speichern einige Parameter in der Konfigurationsdatei der Provider-Objektinstanz, die oben beschrieben wurde, und erhalten sie durch Aufrufen der Funktion vpn_provider_get_string, zum Beispiel:
char * value = vpn_provider_get_string(provider , “Host”)
Dabei ist Provider eine Instanz von struct vpn_provider.
connman_task_add_argument(task, "--dev-name", if_name).
Die obige Zeile zeigt den Namen der virtuellen Schnittstelle, die ConnMan-vpnd initialisiert und zum Lesen und Schreiben von IP-Paketen von der TUN-Schnittstelleninstanz bereitstellt, die für die aktuelle Instanz des VPN-Anbieters ausgelöst wurde. Im Hintergrund bleibt es uns überlassen, das Gerät zu öffnen und einen Dateideskriptor zum Lesen / Schreiben zu erhalten.
Ein kurzer Hinweis: Während der Entwicklung des Plugins stellte sich heraus, dass die TUN-Schnittstelle als Standardroutenschnittstelle festgelegt wurde.
connman_task_add_argument(task, "--dbus-busname", dbus_bus_get_unique_name(connection)); connman_task_add_argument(task, "--dbus-interface", CONNMAN_TASK_INTERFACE); connman_task_add_argument(task, "--dbus-path", connman_task_get_path(task));
Für eine Rückmeldung zwischen der Hintergrundanwendung und dem VPN-Plug-In übergeben wir die ConnManTask-DBus-Adresse und den Pfad an die aktuelle Instanz. Dazu mussten wir in der Initialisierungsfunktion connman_dbus_get_connection aufrufen.
Wir starten den Hintergrundprozess:
err = connman_task_run(task, continent_died, data, &data->stdin_fd, NULL, NULL);
ontin_died - Rückruf, der aufgerufen wird, wenn der Hintergrundprozess beendet wird. Darin finden wir den Fehlercode zum Beenden des Prozesses, Bereitstellen des Speichers und Löschen der hinzugefügten Routen.
notify - Rückruf, der aufgerufen wird, wenn net.connman.Task.notify über DBus aufgerufen wird. Darin erhalten wir DBus-Nachrichten von einer laufenden Hintergrundanwendung. Die Hauptsache ist die Übertragung von Netzwerkparametern: die Adresse der TUN-Schnittstelle, der DNS-Server im virtuellen Netzwerk usw. Netzwerkparameter werden in DBusMessage in Form eines Wörterbuchs gepackt und an die ConnMan-Task übertragen, in der Dbus-Parameter beim Start der Hintergrundanwendung gesendet werden.
Ein Beispiel für die Initialisierung der TUN-Schnittstelle in der Benachrichtigungsfunktion:
struct connman_ipaddress * ipaddress = connman_ipaddress_alloc(AF_INET); connman_ipaddress_set_ipv4(ipaddress, address, netmask, remote_server_ip); connman_ipaddress_set_peer(ipaddress, peer); vpn_provider_set_ipaddress(provider, ipaddress); vpn_provider_set_nameservers(provider, “8.8.8.8”); return VPN_STATE_CONNECT;
Wir übergeben auch Zwischenwerte, wenn wir beispielsweise die Benutzeroberfläche des Ereignisses benachrichtigen möchten, indem wir in die Eigenschaften der aktuellen Verbindung schreiben, die die Benutzeroberfläche von der Methode net.connman.vpn.Connection.GetProperties lernen kann. Beim Ändern von Eigenschaften sendet ConnMan ein DBus-Signal PropertyChanged, zum Beispiel: vpn_provider_set_string (Anbieter, Schlüssel, Wert).
Disconnect - Rückruf, der aufgerufen wird, wenn net.connman.vpn.Connection.Disconnect über DBus aufgerufen wird
Der Stoppvorgang erfolgt durch Senden eines SIGTERM-Signals. Wenn innerhalb von 3 Sekunden keine Trennung erfolgt, wird ein SIGKILL-Signal gesendet.
Entwickeln und Debuggen eines VPN-Plugins
Die Montage des Continent-AP VPN-Plug-Ins und seiner Komponenten erfolgt auf der virtuellen Maschine (Virtual Box) des Sailfish Build Engine-Betriebssystems, die Teil des Sailfish SDK ist. Um das Plugin zu erstellen, benötigen Sie die Bibliotheken: connman-devel, dbus-1, glibs-2.0, die wir installieren, indem wir uns über ssh anmelden:
ssh -p 2222 -i ~/SailfishOS/vmshare/ssh/private_keys/engine/mersdk mersdk@localhost
Wir verwenden das Dienstprogramm sb2 (Scratchbox 2) - Toolkit für die Cross-Kompilierung. Wir installieren die notwendigen Pakete für die Plattformen i486 und armv7hl:
sb2 -t SailfishOS-3.0.1.11-i486 -m sdk-install -R zypper -n in cmake patchelf chrpath connman-devel systemd-compat-libs systemd-devel
sb2 -t SailfishOS-3.0.1.11-armv7hl -m sdk-install -R zypper -n in cmake patchelf chrpath connman-devel systemd-compat-libs systemd-devel
Systemd-compatible-libs und systemd-devel werden für die Ausgabe in das Systemprotokoll mithilfe der Funktion sd_journal_print benötigt. Wir installieren Cmake, da unser Projekt es verwendet, was die Montage für verschiedene Plattformen erheblich vereinfacht.
Wir starten die Montage des VPN-Plugins und seiner Komponenten über sb2 sdk-build:
sb2 -t SailfishOS-3.0.1.11-armv7hl -m sdk-build cmake . && make sb2 -t SailfishOS-3.0.1.11-i486 -m sdk-build cmake . && make
Als Nächstes fügen wir die gesammelten Binärdateien und Bibliotheken in unser UI-Projekt ein, das eine SPEC-Datei zum Generieren des RPM-Pakets für die Sailfish-Distribution von Continent-AP enthält. Dabei wird der Abschnitt zum Installieren unserer Dateien in den Systemordnern des Geräts in der SPEC-Datei leicht angepasst. Beispiel:
%files %defattr(-,root,root,-) %{_sbindir}/continent %{_libdir}/connman/plugins-vpn/continent-proto-plugin.so
Das Plug-In wurde separat von der Benutzeroberfläche des Emulators entwickelt, der Teil des Sailfish SDK ist, und gdbus und journalctl mit der Option -f im Konstantprotokoll-Ausgabemodus haben als Debugging-Tools sehr geholfen.
Beispiel: Erstellen einer Instanz eines Anbieters mit gdbus:
gdbus call --system --dest=net.connman.vpn --object-path / --method net.connman.vpn.Manager.Create "{ 'Type': <'continent'>, … }"
Die Testgeräte waren INOI R7 (Telefon), INOI T8 (Tablet) und ein auf VirtualBox basierender Emulator
Nützliche Links:
- Den für Sailfish angepassten ConnMan-Quellcode finden Sie hier .
- Skelett - Plugin Projekt
- Segelfisch.org/wiki