Desarrollo del complemento VPN Continent-AP para el sistema operativo Sailfish

Introduccion


Trabajo como programador en el departamento de desarrollo y pruebas de herramientas de seguridad para plataformas móviles de la compañía Security Code. El equipo de desarrollo móvil tuvo la tarea de portar la biblioteca multiplataforma Continent-AP de la estación del suscriptor, que ya había operado con éxito en iOS y Android. El principal problema fue que el sistema operativo Sailfish no está tan bien documentado como Android o IOS, pero gracias a los chicos de Open Mobile Platforms que compartieron la documentación.

Arquitectura de la API VPN en el sistema operativo Sailfish


Para todas las conexiones de red en el sistema operativo Sailfish, el servicio ConnMan es responsable, a saber:

  • Escanee redes Wi-Fi y redes celulares y conéctese a ellas;
  • conexión compartida (punto de acceso Wi-Fi);
  • traducción, conexiones de salida al modo de vuelo;
  • Gestión de conexión VPN.

Hablaré sobre el último punto con más detalle. ConnMan tiene varios tipos de complementos VPN predefinidos. Los widgets QML para crear y configurar conexiones están integrados en el firmware.

imagen
Fig. 1 Menú del sistema Sailfish OS para configurar y administrar conexiones VPN

La interfaz de usuario de nuestro cliente VPN es un paquete RPM y durante la instalación no se integra en la ventana del sistema VPN en la sección Configuración, sino que se ve como una aplicación separada. Probablemente habrá un artículo separado sobre el desarrollo de la interfaz de usuario, por lo que la próxima historia será sobre el desarrollo del complemento ConnMan en C / C ++.

imagen
Fig. 2 Continent-AP GUI por Sailfish

VPN-api Sailfish OS consta de los siguientes componentes, mostraremos con el ejemplo de nuestro cliente VPN:

  1. ConnMan es el proceso que comienza cuando se inicia el sistema operativo Sailfish.
  2. connman-vpnd es un proceso demonio lanzado por ConnMan y utilizado para administrar conexiones VPN de varios proveedores, inicializar y desinicializar la interfaz tun, y asignar configuraciones de red (direcciones IP, rutas, servidores DNS) recibidas a través de DBus. En nuestro caso, un proveedor llamado continente.
  3. El complemento VPN continent-proto-plugin.so es una biblioteca que tiene una declaración de macro para cargar en tiempo de ejecución y funciones que se invocan cuando se invocan los métodos de interfaz net.connman.vpn.Connection.
  4. La aplicación en segundo plano (archivo binario / usr / sbin / continent) es un cliente de consola para conectarse al CD (Continent Access Server), que recibe configuraciones de red de él, que pasa a connman-vpnd.
  5. ConnMan Task es un proceso para iniciar, detener y monitorear un cliente de consola en ejecución.
  6. DBus-api: representa connman-vpnd, es decir, net.connman.vpn con las interfaces net.connman.vpn.Manager, net.connman.vpn.Connection.

imagen
Fig. 3 Interacción de componentes entre ellos en el pez vela "Continent-AP"

Complemento VPN


Todos los complementos de terceros que no forman parte de la distribución de ConnMan representan la biblioteca y deben colocarse en / usr / lib / connman / plugins-vpn / cuando se instalan a través del administrador de paquetes.

Un complemento puede tener un archivo de configuración, en el que indicamos desde qué usuario ejecutar el archivo binario y prescribimos sus derechos. El archivo debe ubicarse en el sistema a lo largo de la ruta /etc/connman/vpn-plugin/continent.conf, y su nombre debe corresponder con el nombre de nuestro proveedor, en nuestro caso continent.conf.

Contenido del archivo, por ejemplo:

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

El complemento continent-proto-plugin.so en ConnMan se registra utilizando la macro CONNMAN_PLUGIN_DEFINE (nombre, descripción, versión, init, salida), en nuestro ejemplo, la llamada a la macro se verá así:

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

El argumento nombre (continente) debe estar sin comillas. Las funciones continent_init, continent_exit se invocan cuando el complemento se carga y descarga, por ejemplo, cuando se llama a systemctl restart connman durante la instalación de RPM. La función continent_init tiene una llamada a las funciones vpn_register y connman_dbus_get_connection.

 vpn_register(name, driver, binary_path) 

nombre: el nombre del proveedor que se está registrando, en nuestro caso es "continente";
driver - struct estructura vpn_driver que contiene punteros a funciones de devolución de llamada, por ejemplo, al acceder al complemento a través de DBus;
binary_path - ruta al archivo binario, en nuestro caso es "/ usr / sbin / continent".

La función connman_dbus_get_connection le permite obtener la conexión DBus establecida, la conexión DBusConnection *.

Se requiere la función continent_exit para cancelar el registro del complemento en ConnMan y cerrar la conexión DBus.

Se crea una instancia de proveedor de VPN cuando DBus llama al método net.connman.vpn.Manager.Create, se crea automáticamente un archivo de configuración para él en el directorio /var/lib/connman/provider_${Hostasket_{VPN.Domain}. El proveedor se elimina llamando a net.connman.vpn.Manager.Remove. Cuando se llama al método net.connman.vpn.Connection.Connect, la configuración se carga en el proveedor struct vpn_provider * creado.

También me gustaría hablar sobre algunas funciones en struct vpn_driver, algunas de ellas son necesarias para la implementación.

connect - callback, llamado cuando net.connman.vpn.Connection.Connect se llama con la dirección correspondiente del objeto DBus a través de DBus, tiene la firma:

 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) 

El segundo argumento para esta devolución de llamada es una tarea struct connman_task *, ejecutará el archivo binario, pero debe pasar argumentos antes de comenzar, por ejemplo, el host y el puerto del servidor:

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

Almacenamos algunos parámetros en el archivo de configuración de la instancia del objeto proveedor, se describió anteriormente, y lo obtenemos llamando a la función vpn_provider_get_string, por ejemplo:
 char * value = vpn_provider_get_string(provider , “Host”) 

donde el proveedor es una instancia de struct vpn_provider.

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

La línea anterior ilustra el nombre de la interfaz virtual que ConnMan-vpnd inicializa y proporciona para leer y escribir paquetes IP desde la instancia de la interfaz TUN generada para la instancia actual del proveedor de VPN. En el proceso en segundo plano, nos queda por abrir el dispositivo y obtener un descriptor de archivo para leer / escribir.

Una breve nota: durante el desarrollo del complemento resultó que la interfaz TUN se genera como la interfaz de ruta predeterminada.

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

Para obtener comentarios entre la aplicación en segundo plano y el complemento VPN, pasamos la dirección y la ruta de DBn de ConnManTask a la instancia actual, para esto necesitamos llamar a connman_dbus_get_connection en la función de inicialización.

Comenzamos el proceso de fondo:

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

continent_died: devolución de llamada cuando el proceso en segundo plano finaliza. En él, descubrimos el código de error para terminar el proceso, desplegar la memoria, eliminar las rutas agregadas.

notify: devolución de llamada, llamada cuando se llama a net.connman.Task.notify a través de DBus, en él recibimos mensajes DBus de una aplicación en segundo plano en ejecución. Lo principal es la transmisión de parámetros de red: la dirección de la interfaz TUN, el servidor DNS en la red virtual, etc. Los parámetros de red se empaquetan en DBusMessage en forma de diccionario y se transfieren a la tarea ConnMan, en la que se envían los parámetros Dbus cuando se inicia la aplicación en segundo plano.

Un ejemplo de inicialización de la interfaz TUN en la función de notificación:

 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; 

También pasamos valores intermedios, por ejemplo, si queremos notificar a la UI del evento escribiendo a las propiedades de la conexión actual, que la UI puede aprender del método net.connman.vpn.Connection.GetProperties, al cambiar Propiedades, ConnMan envía una señal DBus PropertyChanged, por ejemplo: vpn_provider_set_string (proveedor, clave, valor).

desconectar: ​​devolución de llamada que se llama cuando net.connman.vpn.Connection.Disconnect se llama a través de DBus
El proceso de detención se produce enviando una señal SIGTERM; si no se produce la desconexión en 3 segundos, se envía una señal SIGKILL.

Desarrollar y depurar un complemento VPN


El ensamblaje del complemento VPN Continent-AP y sus componentes se realiza en la máquina virtual del sistema operativo Sailfish Build Engine (Virtual Box), que forma parte del SDK de Sailfish. Para compilar el complemento, necesita las bibliotecas: connman-devel, dbus-1, glibs-2.0, que instalamos iniciando sesión a través de ssh:

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

Utilizamos la utilidad sb2 (Scratchbox 2) - kit de herramientas para compilación cruzada. Instalamos los paquetes necesarios para las plataformas i486 y 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 y systemd-devel son necesarios para la salida al registro del sistema utilizando la función sd_journal_print. Instalamos Cmake, ya que nuestro proyecto lo usa, esto simplifica enormemente el ensamblaje para diferentes plataformas.

Comenzamos el ensamblaje del complemento VPN y sus componentes a través de 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 


A continuación, colocamos los archivos y bibliotecas binarios recopilados en nuestro proyecto de interfaz de usuario, que contiene el archivo SPEC para generar el paquete Continent-AP Sailfish RPM, ajustando ligeramente la sección para instalar nuestros archivos en las carpetas del sistema del dispositivo en el archivo SPEC, por ejemplo:

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

El complemento se desarrolló por separado de la interfaz de usuario en el emulador, que es parte del SDK de Sailfish, y gdbus y journalctl con la opción -f en el modo de salida de registro constante ayudaron mucho como herramientas de depuración.

Por ejemplo, crear una instancia de un proveedor usando gdbus:

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

Los dispositivos de prueba fueron INOI R7 (teléfono), INOI T8 (tableta) y un emulador basado en VirtualBox

Enlaces utiles:


  1. El código fuente de ConnMan adaptado para Sailfish se puede encontrar aquí .
  2. Esqueleto - proyecto de complemento
  3. sailfishos.org/wiki

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


All Articles