Développement du plugin VPN Continent-AP pour Sailfish OS

Présentation


Je travaille en tant que programmeur dans le département de développement et de test d'outils de sécurité pour les plateformes mobiles de la société Security Code. L'équipe de développement mobile a été chargée de porter la bibliothèque multiplateforme Continent-AP de la station d'abonné, qui avait déjà réussi à fonctionner sur IOS et Android. Le principal problème était que le système d'exploitation Sailfish n'est pas aussi bien documenté qu'Android ou IOS, mais grâce aux gars d'Open Mobile Platforms qui ont partagé la documentation.

Architecture de l'API VPN dans Sailfish OS


Pour toutes les connexions réseau dans Sailfish OS, le service ConnMan est responsable, à savoir:

  • Scannez les réseaux Wi-Fi et les réseaux cellulaires et connectez-vous à eux;
  • partage de connexion (point d'accès Wi-Fi);
  • traduction, connexions de sortie en mode avion;
  • Gestion des connexions VPN.

Je vais parler du dernier point plus en détail. ConnMan dispose de plusieurs types de plugins VPN prédéfinis. Les widgets QML pour créer et configurer des connexions sont intégrés dans le firmware.

image
Fig. 1 Menu système Sailfish OS pour configurer et gérer les connexions VPN

L'interface utilisateur de notre client VPN est un package RPM et lors de l'installation, il ne s'intègre pas dans la fenêtre du système VPN dans la section Paramètres, mais ressemble à une application distincte. Il y aura probablement un article séparé sur le développement de l'interface utilisateur, donc la prochaine histoire portera sur le développement du plug-in ConnMan en C / C ++.

image
Fig. 2 Continent-AP GUI par Sailfish

VPN-api Sailfish OS se compose des composants suivants, nous allons le montrer par l'exemple de notre client VPN:

  1. ConnMan est le processus qui démarre au démarrage de Sailfish OS.
  2. connman-vpnd est un processus démon lancé par ConnMan et utilisé pour gérer les connexions VPN de divers fournisseurs, initialiser et désinitialiser l'interface tun, lui attribuer les paramètres réseau (adresses IP, routes, serveurs DNS) reçus via DBus. Dans notre cas, un fournisseur nommé continent.
  3. Le plugin VPN continent-proto-plugin.so est une bibliothèque qui a une déclaration de macro pour le chargement dans le runtime et des fonctions qui sont appelées lorsque les méthodes d'interface net.connman.vpn.Connection sont appelées.
  4. L'application d'arrière-plan (fichier binaire / usr / sbin / continent) est un client de console pour se connecter au CD (Continent Access Server), recevoir les paramètres réseau de celui-ci, qu'il transmet à connman-vpnd.
  5. La tâche ConnMan est un processus permettant de démarrer, d'arrêter et de surveiller un client de console en cours d'exécution.
  6. DBus-api - représente connman-vpnd, à savoir net.connman.vpn avec les interfaces net.connman.vpn.Manager, net.connman.vpn.Connection.

image
Fig. 3 Interaction des composants entre eux dans Sailfish "Continent-AP"

Plugin VPN


Tous les plugins tiers qui ne font pas partie de la distribution ConnMan représentent la bibliothèque et doivent être placés dans / usr / lib / connman / plugins-vpn / lors de l'installation via le gestionnaire de packages.

Un plugin peut avoir un fichier de configuration, dans lequel nous indiquons à partir de quel utilisateur exécuter le fichier binaire et prescrivons ses droits. Le fichier doit être situé dans le système le long du chemin /etc/connman/vpn-plugin/continent.conf, et son nom doit correspondre au nom de notre fournisseur, dans notre cas continent.conf.

Contenu du fichier par exemple:

[VPN Binary] User = nemo Group = vpn SupplementaryGroups = inet,net_admin 

Le plugin continent-proto-plugin.so dans ConnMan est enregistré à l'aide de la macro CONNMAN_PLUGIN_DEFINE (nom, description, version, init, exit), dans notre exemple, l'appel de macro ressemblera à ceci:

 CONNMAN_PLUGIN_DEFINE(continent, "continent VPN plugin", CONNMAN_VERSION, CONNMAN_PLUGIN_PRIORITY_DEFAULT, continent_init, continent_exit); 

L'argument nom (continent) doit être sans guillemets. Les fonctions continent_init, continent_exit sont appelées lorsque le plugin est chargé et déchargé, par exemple, lorsque systemctl restart connman est appelé pendant l'installation de RPM. La fonction continent_init a un appel aux fonctions vpn_register et connman_dbus_get_connection.

 vpn_register(name, driver, binary_path) 

nom - le nom du fournisseur enregistré, dans notre cas c'est «continent»;
driver - struct structure vpn_driver contenant des pointeurs vers des fonctions de rappel, par exemple, lors de l'accès au plugin via DBus;
binary_path - chemin d'accès au fichier binaire, dans notre cas, il s'agit de «/ usr / sbin / continent».

La fonction connman_dbus_get_connection vous permet d'obtenir la connexion DBus établie, la connexion DBusConnection *.

La fonction continent_exit est requise pour désenregistrer le plugin dans ConnMan et fermer la connexion DBus.

Une instance de fournisseur VPN est créée lorsque DBus appelle la méthode net.connman.vpn.Manager.Create, un fichier de paramètres est automatiquement créé pour elle dans le répertoire /var/lib/connman/provider_${Hostasket_{VPN.Domain}. Le fournisseur est supprimé en appelant net.connman.vpn.Manager.Remove. Lorsque la méthode net.connman.vpn.Connection.Connect est appelée, les paramètres sont chargés dans le fournisseur struct vpn_provider * créé.

Je voudrais également parler de certaines fonctions de la structure vpn_driver, certaines d'entre elles sont requises pour la mise en œuvre.

connect - le rappel, appelé lorsque net.connman.vpn.Connection.Connect est appelé avec l'adresse correspondante de l'objet DBus via DBus, a la signature:

 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) 

Le deuxième argument de ce rappel est une tâche struct connman_task *, il exécutera le fichier binaire, mais vous devez passer des arguments avant de démarrer, par exemple, l'hôte et le port du serveur:

 connman_task_add_argument(task, "--host", value); connman_task_add_argument(task, "--port", value); 

Nous stockons certains paramètres dans le fichier de configuration de l'instance d'objet fournisseur, il a été décrit ci-dessus, et nous les obtenons en appelant la fonction vpn_provider_get_string, par exemple:
 char * value = vpn_provider_get_string(provider , “Host”) 

où fournisseur est une instance de struct vpn_provider.

 connman_task_add_argument(task, "--dev-name", if_name). 

La ligne ci-dessus illustre le nom de l'interface virtuelle que ConnMan-vpnd initialise et fournit pour lire et écrire des paquets IP à partir de l'instance d'interface TUN déclenchée pour l'instance actuelle du fournisseur VPN. En arrière-plan, il nous reste à ouvrir l'appareil et à obtenir un descripteur de fichier pour la lecture / écriture.

Une petite note: pendant le développement du plugin, il s'est avéré que l'interface TUN est élevée comme interface de route par défaut.

 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)); 

Pour les commentaires entre l'application d'arrière-plan et le plug-in VPN, nous transmettons l'adresse et le chemin ConnManTask DBus à l'instance actuelle, pour cela, nous devions appeler connman_dbus_get_connection dans la fonction d'initialisation.

Nous commençons le processus d'arrière-plan:

 err = connman_task_run(task, continent_died, data, &data->stdin_fd, NULL, NULL); 

continent_died - rappel appelé à la fin du processus d'arrière-plan. Dans ce document, nous découvrons le code d'erreur pour terminer le processus, déployer la mémoire, supprimer les routes ajoutées.

notify - callback, appelé lorsque net.connman.Task.notify est appelé via DBus, nous y recevons des messages DBus d'une application d'arrière-plan en cours d'exécution. L'essentiel est la transmission des paramètres réseau: adresse de l'interface TUN, serveur DNS dans le réseau virtuel, etc. Les paramètres réseau sont regroupés dans DBusMessage sous la forme d'un dictionnaire et transférés vers la tâche ConnMan, dans laquelle les paramètres Dbus sont envoyés au lancement de l'application en arrière-plan.

Un exemple d'initialisation de l'interface TUN dans la fonction de notification:

 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; 

Nous transmettons également des valeurs intermédiaires, par exemple, si nous voulons notifier l'interface utilisateur de l'événement en écrivant dans les propriétés de la connexion actuelle, que l'interface utilisateur peut apprendre de la méthode net.connman.vpn.Connection.GetProperties, lors de la modification des propriétés, ConnMan envoie un signal DBus PropertyChanged, par exemple: vpn_provider_set_string (fournisseur, clé, valeur).

déconnecter - rappel qui est appelé lorsque net.connman.vpn.Connection.Disconnect est appelé via DBus
Le processus d'arrêt se produit en envoyant un signal SIGTERM, si aucune déconnexion ne se produit dans les 3 secondes, un signal SIGKILL est envoyé.

Développer et déboguer un plugin VPN


L'assemblage du plug-in VPN Continent-AP et de ses composants est effectué sur la machine virtuelle Sailfish Build Engine OS (Virtual Box), qui fait partie du SDK Sailfish. Pour construire le plugin, vous avez besoin des bibliothèques: connman-devel, dbus-1, glibs-2.0, que nous installons en vous connectant via ssh:

 ssh -p 2222 -i ~/SailfishOS/vmshare/ssh/private_keys/engine/mersdk mersdk@localhost 

Nous utilisons l'utilitaire sb2 (Scratchbox 2) - boîte à outils pour la compilation croisée. Nous installons les packages nécessaires pour les plates-formes i486 et 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-compat-libs et systemd-devel sont nécessaires pour la sortie dans le journal système à l'aide de la fonction sd_journal_print. Nous installons Cmake, puisque notre projet l'utilise, cela simplifie grandement l'assemblage pour différentes plates-formes.

Nous commençons l'assemblage du plugin VPN et de ses composants via 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 


Ensuite, nous avons mis les fichiers binaires et les bibliothèques collectés dans notre projet d'interface utilisateur, qui contient un fichier SPEC pour générer le package RPM de distribution Continent-AP Sailfish, ajustant légèrement la section pour installer nos fichiers dans les dossiers système de l'appareil dans le fichier SPEC, par exemple:

 %files %defattr(-,root,root,-) %{_sbindir}/continent %{_libdir}/connman/plugins-vpn/continent-proto-plugin.so 

Le plug-in a été développé séparément de l'interface utilisateur de l'émulateur, qui fait partie du SDK Sailfish, et gdbus et journalctl avec l'option -f en mode de sortie de journal constant ont beaucoup aidé en tant qu'outils de débogage.

Par exemple, créer une instance d'un fournisseur à l'aide de gdbus:

 gdbus call --system --dest=net.connman.vpn --object-path / --method net.connman.vpn.Manager.Create "{ 'Type': <'continent'>, … }" 

Les appareils de test étaient INOI R7 (téléphone), INOI T8 (tablette) et un émulateur basé sur VirtualBox

Liens utiles:


  1. Le code source ConnMan adapté pour Sailfish peut être trouvé ici .
  2. Skeleton - projet de plugin
  3. sailfishos.org/wiki

Source: https://habr.com/ru/post/fr471306/


All Articles