Cómo comenzar a usar User Mode en Linux

Introducción del traductor: En medio de la entrada masiva de contenedores de todo tipo en nuestras vidas, puede ser bastante interesante y útil saber de qué tecnologías comenzó todo esto alguna vez. Algunas de ellas todavía se pueden aplicar con éxito hoy en día, pero no todos recuerdan (o saben, si no vivieron durante su desarrollo intenso) estos métodos. Una de estas tecnologías es User Mode Linux. El autor del original investigó bastante, averiguando qué de los antiguos desarrollos sigue funcionando y qué ya no. Reunió una especie de guía paso a paso sobre cómo establecer un UML casero en 2019. Y sí, hemos invitado al autor del post original a Habr. Cadey, así que si tienes preguntas, pregúntalas en inglés en los comentarios.

Cómo comenzar a usar User Mode en Linux

User Mode en Linux es, de hecho, un puerto del núcleo de Linux sobre sí mismo. Este modo permite ejecutar un núcleo completo de Linux como un proceso de usuario y suele ser utilizado por desarrolladores para probar controladores. Pero este modo también es útil como herramienta de aislamiento general, cuyo principio es similar al funcionamiento de máquinas virtuales. Este modo proporciona un mayor aislamiento que Docker, pero menor que una máquina virtual completa como KVM o Virtual Box.

En general, User Mode puede parecer una herramienta extraña y complicada de usar, pero aún tiene sus áreas de aplicación. Después de todo, es un núcleo de Linux completo que funciona desde un usuario no privilegiado. Esta característica permite ejecutar código potencialmente no seguro sin amenazar a la máquina host. Y dado que es un núcleo completo, sus procesos están aislados de la máquina host, es decir, los procesos que se ejecutan dentro de User Mode no serán visibles para el host. Esto no es lo mismo que un contenedor Docker convencional, donde la máquina host siempre ve los procesos dentro del almacenamiento. Observa este fragmento de pstree de uno de mis servidores:

containerd─┬─containerd-shim─┬─tini─┬─dnsd───19*[{dnsd}]
           │                 │      └─s6-svscan───s6-supervise
           │                 └─10*[{containerd-shim}]
           ├─containerd-shim─┬─tini─┬─aerial───21*[{aerial}]
           │                 │      └─s6-svscan───s6-supervise
           │                 └─10*[{containerd-shim}]
           ├─containerd-shim─┬─tini─┬─s6-svscan───s6-supervise
           │                 │      └─surl
           │                 └─9*[{containerd-shim}]
           ├─containerd-shim─┬─tini─┬─h───13*[{h}]
           │                 │      └─s6-svscan───s6-supervise
           │                 └─10*[{containerd-shim}]
           ├─containerd-shim─┬─goproxy───14*[{goproxy}]
           │                 └─9*[{containerd-shim}]
           └─32*[{containerd}]

Y compáralo con pstree del núcleo de Linux en Modo Usuario:

linux─┬─5*[linux]
      └─slirp

Al trabajar con contenedores Docker, puedo ver desde el host los nombres de los procesos que se ejecutan en el sistema invitado. Con el Modo Usuario de Linux esto no es posible. ¿Qué significa esto? Significa que las herramientas de monitoreo que funcionan a través del subsistema de auditoría de Linux no ven los procesos que se ejecutan en el sistema invitado. Pero en algunas situaciones, esta característica puede convertirse en un arma de doble filo.

En general, toda la entrada a continuación es un conjunto de investigaciones y torpes intentos de obtener el resultado deseado. Para ello, tuve que utilizar diferentes herramientas antiguas, leer el código fuente del núcleo, realizar intensas depuraciones de código escrito en una época en la que aún estaba en la escuela primaria, así como hurgar en las compilaciones de Heroku con un binario especial en busca de las herramientas que necesitaba. Todo este trabajo llevó a que los chicos en mi IRC comenzaron a llamarme la maga (magic). Espero que esta entrada sirva como una documentación confiable para que alguien pueda lograr todo lo mismo, pero ya con núcleos y versiones de SO más nuevos.

Configuración

La configuración del Modo Usuario de Linux se lleva a cabo en varias etapas:

  • instalación de dependencias en el host;
  • descarga del núcleo de Linux;
  • configuración de la compilación del núcleo;
  • compilación del núcleo;
  • instalación del binario;
  • configuración del sistema de archivos del invitado;
  • ajuste de los parámetros de arranque del núcleo;
  • configuración de la red del invitado;
  • ejecución del núcleo del invitado.

Supongo que si decides hacerlo todo tú mismo, probablemente lo harás en un sistema similar a Ubuntu o Debian. Intenté implementar todo lo mencionado anteriormente en mi distribución favorita, Alpine, pero no tuve suerte, aparentemente porque el núcleo de Linux tiene una dependencia rígida de glibc-isms para los controladores en modo usuario. Planeo reportar esto upstream después de entender completamente el problema.

Instalación de dependencias en el host

Ubuntu requiere, como mínimo, los siguientes paquetes para compilar el núcleo de Linux (en caso de una instalación limpia):

- 'build-essential'
- 'flex'
- 'bison'
- 'xz-utils'
- 'wget'
- 'ca-certificates'
- 'bc'
- 'linux-headers'

Puedes instalarlos usando el siguiente comando (con privilegios de root o usando sudo):

apt-get -y install build-essential flex bison xz-utils wget ca-certificates bc 
                   linux-headers-$(uname -r)

Ten en cuenta que ejecutar el programa de configuración del menú para el núcleo de Linux requerirá la instalación de libncurses-dev. Por favor, asegúrate de que esté instalado con el siguiente comando (con privilegios de root o usando sudo):

apt-get -y install libncurses-dev

Descarga del núcleo

Define un lugar para descargar y compilar el núcleo. Para esta operación, necesitarás alrededor de 1,3 GB de espacio en disco duro, así que asegúrate de tenerlo disponible.

Luego ve a kernel.org y obtén la URL para descargar la última versión estable del núcleo. En el momento de escribir esta publicación, es: https://cdn.kernel.org/pub/linux/kernel/v5.x/linux-5.1.16.tar.xz

Descarga este archivo usando 'wget':

wget https://cdn.kernel.org/pub/linux/kernel/v5.x/linux-5.1.16.tar.xz

Y extráelo usando 'tar':

tar xJf linux-5.1.16.tar.xz

Ahora ingresa al directorio creado al descomprimir el tarball:

cd linux-5.1.16

Configuración de la compilación del núcleo

El sistema de compilación del núcleo es un conjunto de Makefiles con con un montón de herramientas y scripts personalizados para automatizar el proceso. Para comenzar, abre el programa de configuración interactivo:

make ARCH=um menuconfig

Esto realizará parcialmente la compilación y te mostrará un cuadro de diálogo. Cuando en la parte inferior de la ventana aparezca ‘[Select]‘, podrás comenzar a configurar usando las teclas Espacio o Enter. La navegación por la ventana es como de costumbre, con las teclas de flecha ‘arriba’ y ‘abajo’, y la selección de elementos con ‘izquierda’ o ‘derecha’.

El indicador ‘->’ significa que estás en un submenú, al cual se accede con la tecla Enter. La salida de este se realiza, evidentemente, a través de ‘[Exit]‘.

Activa las siguientes opciones en ‘[Select]‘ y asegúrate de que haya un símbolo ‘[*]’ junto a ellas:

Opciones específicas de UML:
  - Sistema de archivos del host
Soporte de red (habilite esto para que se muestre el submenú):
  - Opciones de red:
    - Red TCP/IP
Dispositivos de red UML:
  - Dispositivo de red virtual
  - Transporte SLiRP

Todo, desde esta ventana se puede salir seleccionando consecutivamente ‘[Exit]‘. Solo asegúrese de que al final se le ofrezca guardar la configuración y seleccione ‘[Sí]‘.

Le recomiendo que experimente con los parámetros de compilación del núcleo después de leer esta publicación. Gracias a estos experimentos, podrá aprender mucho sobre el funcionamiento de los mecanismos de bajo nivel del núcleo y el impacto de diferentes banderas en su compilación.

Compilación del núcleo

El núcleo de Linux es un programa grande que se ocupa de muchas cosas. Incluso con una configuración mínima en un hardware antiguo, su compilación puede tardar un buen tiempo. Así que compile el núcleo usando el siguiente comando:

make ARCH=um -j$(nproc)

¿Por qué? Este comando le indicará a nuestro compilador que utilice todos los núcleos y threads de procesador disponibles durante el proceso de compilación. El comando $(nproc) al final de Build inserta la salida del comando nproc, que es parte de coreutils en la instalación estándar de Ubuntu.

Después de un tiempo, nuestro núcleo será compilado en un archivo ejecutable ./linux.

Instalación del binario

Dado que User Mode en Linux crea un binario normal, puede instalarlo como cualquier otra utilidad. Así es como lo hice:

mkdir -p ~/bin
cp linux ~/bin/linux

También debe asegurarse de que ~/bin esté en su $PATH:

export PATH=$PATH:$HOME/bin

Configuración del sistema de archivos del invitado

Cree un directorio para el sistema de archivos del invitado:

mkdir -p $HOME/prefix/uml-demo
cd $HOME/prefix

Visite alpinelinux.org y en la sección de descargas encuentre el enlace actualizado para descargar MINI ROOT FILESYSTEM. En el momento de escribir esta publicación, era:

http://dl-cdn.alpinelinux.org/alpine/v3.10/releases/x86_64/alpine-minirootfs-3.10.0-x86_64.tar.gz

Descargue este tarball con wget:

wget -O alpine-rootfs.tgz http://dl-cdn.alpinelinux.org/alpine/v3.10/releases/x86_64/alpine-minirootfs-3.10.0-x86_64.tar.gz

Ahora ingresa en el directorio del sistema de archivos del invitado y descomprime el archivo:

cd uml-demo
tar xf ../alpine-rootfs.tgz

Las acciones descritas crearán un pequeño esquema del sistema de archivos. Debido a la naturaleza de la operación del sistema, será extremadamente complicado instalar paquetes a través del gestor apk de Alpine. Pero este FS será suficiente para evaluar la idea general.

También necesitaremos la herramienta tini para evitar el consumo de memoria de procesos zombis de nuestro núcleo invitado.

wget -O tini https://github.com/krallin/tini/releases/download/v0.18.0/tini-static
chmod +x tini

Creación de la línea de comandos del núcleo

En el núcleo de Linux, al igual que en la mayoría de otros programas, hay argumentos de línea de comandos que se pueden consultar especificando la clave --help.

Este es el --help

linux --help
User Mode Linux v5.1.16
        disponible en http://user-mode-linux.sourceforge.net/

--showconfig
    Imprime el archivo de configuración con el que este binario UML fue generado.

iomem=,
    Configura  como una región de memoria IO llamada .

mem=
    Esto controla cuánta "memoria física" el kernel asigna
    para el sistema. El tamaño se especifica como un número seguido de
    uno de 'k', 'K', 'm', 'M', que tienen los significados obvios.
    Esto no está relacionado con la cantidad de memoria en el host. Puede
    ser más, y el exceso, si alguna vez se utiliza, simplemente será intercambiado.
        Ejemplo: mem=64M

--help
    Imprime este mensaje.

debug
    esta bandera no es necesaria para ejecutar gdb en UML en modo skas

root=
    Esto es realmente utilizado por el kernel genérico de la misma
    manera que en cualquier otro kernel. Si configuras varios dispositivos de bloque
    y quieres arrancar desde algo diferente a ubd0, usarías algo como:
        root=/dev/ubd5

--version
    Imprime el número de versión del kernel.

umid=
    Esto se utiliza para asignar una identidad única a esta máquina UML y
    se utiliza para nombrar el archivo pid y el socket de consola de gestión.

con[0-9]*=
    Adjunta una consola o línea serial a un canal host. Ver
    http://user-mode-linux.sourceforge.net/old/input.html para una descripción completa de este interruptor.

eth[0-9]+=,
    Configura un dispositivo de red.
    
aio=2.4
    Esto se utiliza para obligar a UML a usar AIO estilo 2.4 incluso cuando AIO 2.6 está
    disponible. AIO 2.4 es un hilo único que maneja una solicitud a la vez,
    de forma síncrona. AIO 2.6 es un hilo que utiliza la interfaz AIO 2.6
    para manejar un número arbitrario de solicitudes pendientes. AIO 2.6
    no está disponible en modo tt, en hosts 2.4 o cuando UML se compila con
    /usr/include/linux/aio_abi.h no disponible. Muchas distribuciones no
    incluyen aio_abi.h, así que necesitarás copiarlo desde un árbol de kernel a
    tu /usr/include/linux para poder construir un UML compatible con AIO

nosysemu
    Desactiva el parche de emulación de syscall para ptrace (SYSEMU).
    SYSEMU es un parche de rendimiento introducido por Laurent Vivier. Cambia
    el comportamiento de ptrace() y ayuda a reducir las tasas de cambio de contexto en el host.
    Para que funcione, necesitas un parche de kernel para tu host también.
    Ver http://perso.wanadoo.fr/laurent.vivier/UML/ para más información.

uml_dir=
    La ubicación para colocar los archivos pid y umid.

quiet
    Desactiva los mensajes de información durante el arranque.

hostfs=,,...
    Esto se utiliza para establecer parámetros de hostfs. El argumento del directorio raíz
    se utiliza para restringir todos los montajes de hostfs a dentro del árbol de directorios especificado
    en el host. Si esto no se especifica, entonces un usuario dentro de UML puede
    montar cualquier cosa en el host que sea accesible para el usuario que lo está
    ejecutando.
    La única bandera actualmente soportada es 'append', que especifica que todos
    los archivos abiertos por hostfs serán abiertos en modo de adición.

Este conjunto ilumina los parámetros básicos de inicio. Vamos a ejecutar el núcleo con el conjunto mínimo de opciones necesarias:

linux 
  root=/dev/root 
  rootfstype=hostfs 
  rootflags=$HOME/prefix/uml-demo 
  rw 
  mem=64M 
  init=/bin/sh

Las líneas anteriores le dicen a nuestro núcleo lo siguiente:

  • Supongamos que el sistema de archivos raíz es un dispositivo pseudo /dev/root.
  • Elige hostfs como controlador del sistema de archivos raíz.
  • Monta el sistema de archivos invitado que creamos en el dispositivo raíz.
  • Y sí, en modo de lectura-escritura.
  • Usa solo 64 megabytes de RAM (puedes usar mucho menos, dependiendo de lo que planeas hacer, pero 64 MB parecen ser una cantidad óptima).
  • El núcleo se inicia automáticamente /bin/sh cómo init-proceso.

Ejecuta este comando, y deberías obtener algo como lo siguiente:

Otra hoja

Límites de volcado de núcleo:
        suave - 0
        duro - NINGUNO
Comprobando que ptrace puede cambiar los números de llamadas al sistema... OK
Comprobando el parche de emulación de llamadas al sistema para ptrace... OK
Comprobando el parche de emulación avanzada de llamadas al sistema para ptrace... OK
Comprobando las variables de entorno para un tempdir... no se encontró ninguno
Comprobando si /dev/shm está en tmpfs... OK
Comprobando PROT_EXEC mmap en /dev/shm... OK
Añadiendo 32137216 bytes a la memoria física para contabilizar el gap de exec-shield
Versión de Linux 5.1.16 (cadey@kahless) (versión de gcc 7.4.0 (Ubuntu 7.4.0-1ubuntu1~18.04.1)) #30 Dom Jul 7 18:57:19 UTC 2019
Construidas 1 listas de zona, agrupación de movilidad activada.  Total de páginas: 23898
Línea de comandos del kernel: root=/dev/root rootflags=/home/cadey/dl/uml/alpine rootfstype=hostfs rw mem=64M init=/bin/sh
Entradas de la tabla hash de caché de dentry: 16384 (orden: 5, 131072 bytes)
Entradas de la tabla hash de caché de inodo: 8192 (orden: 4, 65536 bytes)
Memoria: 59584K/96920K disponibles (2692K código del kernel, 708K rwdata, 588K rodata, 104K init, 244K bss, 37336K reservados, 0K reservados para cma)
SLUB: HWalign=64, Orden=0-3, MinObjects=0, CPUs=1, Nodos=1
NR_IRQS: 15
fuente de reloj: temporizador: máscara: 0xffffffffffffffff ciclos máximos: 0x1cd42e205, ns_max_idle: 881590404426 ns
Calibrando el bucle de retraso... 7479.29 BogoMIPS (lpj=37396480)
pid_max: predeterminado: 32768 mínimo: 301
Entradas de la tabla hash de caché de montaje: 512 (orden: 0, 4096 bytes)
Entradas de la tabla hash de caché de punto de montaje: 512 (orden: 0, 4096 bytes)
Comprobando que los ptys de host soportan salida SIGIO... Sí
Comprobando que los ptys de host soportan SIGIO al cerrar... No, habilitando solución alternativa
devtmpfs: inicializado
random: get_random_bytes llamado desde setup_net+0x48/0x1e0 con crng_init=0
Usando AIO de host 2.6
fuente de reloj: jiffies: máscara: 0xffffffff ciclos máximos: 0xffffffff, ns_max_idle: 19112604462750000 ns
Entradas de la tabla hash de futex: 256 (orden: 0, 6144 bytes)
NET: Protocolo de familia 16 registrado
fuente de reloj: Cambiado a fuente de reloj temporizador
NET: Protocolo de familia 2 registrado
tcp_listen_portaddr_hash entradas de la tabla hash: 256 (orden: 0, 4096 bytes)
Entradas de la tabla hash de TCP establecido: 1024 (orden: 1, 8192 bytes)
Entradas de la tabla hash de vinculación de TCP: 1024 (orden: 1, 8192 bytes)
TCP: Tablas hash configuradas (establecido 1024 vinculación 1024)
Entradas de la tabla hash de UDP: 256 (orden: 1, 8192 bytes)
Entradas de la tabla hash de UDP-Lite: 256 (orden: 1, 8192 bytes)
NET: Protocolo de familia 1 registrado
consola [stderr0] desactivada
mconsole (versión 2) inicializada en /home/cadey/.uml/tEwIjm/mconsole
Comprobando soporte de host MADV_REMOVE... OK
workingset: timestamp_bits=62 max_order=14 bucket_order=0
Capa de bloque SCSI genérico (bsg) versión 0.4 cargada (mayor 254)
scheduler de E/S noop registrado (predeterminado)
scheduler de E/S bfq registrado
bucle: módulo cargado
NET: Protocolo de familia 17 registrado
Controlador de consola stdio inicializado
Usando un tipo de canal que está configurado fuera de UML
setup_one_line falló para el dispositivo 1: Configuración fallida
Usando un tipo de canal que está configurado fuera de UML
setup_one_line falló para el dispositivo 2: Configuración fallida
Usando un tipo de canal que está configurado fuera de UML
setup_one_line falló para el dispositivo 3: Configuración fallida
Usando un tipo de canal que está configurado fuera de UML
setup_one_line falló para el dispositivo 4: Configuración fallida
Usando un tipo de canal que está configurado fuera de UML
setup_one_line falló para el dispositivo 5: Configuración fallida
Usando un tipo de canal que está configurado fuera de UML
setup_one_line falló para el dispositivo 6: Configuración fallida
Usando un tipo de canal que está configurado fuera de UML
setup_one_line falló para el dispositivo 7: Configuración fallida
Usando un tipo de canal que está configurado fuera de UML
setup_one_line falló para el dispositivo 8: Configuración fallida
Usando un tipo de canal que está configurado fuera de UML
setup_one_line falló para el dispositivo 9: Configuración fallida
Usando un tipo de canal que está configurado fuera de UML
setup_one_line falló para el dispositivo 10: Configuración fallida
Usando un tipo de canal que está configurado fuera de UML
setup_one_line falló para el dispositivo 11: Configuración fallida
Usando un tipo de canal que está configurado fuera de UML
setup_one_line falló para el dispositivo 12: Configuración fallida
Usando un tipo de canal que está configurado fuera de UML
setup_one_line falló para el dispositivo 13: Configuración fallida
Usando un tipo de canal que está configurado fuera de UML
setup_one_line falló para el dispositivo 14: Configuración fallida
Usando un tipo de canal que está configurado fuera de UML
setup_one_line falló para el dispositivo 15: Configuración fallida
Consola inicializada en /dev/tty0
consola [tty0] habilitada
consola [mc-1] habilitada
Falló al inicializar el dispositivo ubd 0: No se pudo determinar el tamaño del archivo del dispositivo
VFS: Raíz montada (sistema de archivos hostfs) en el dispositivo 0:11.
devtmpfs: montado
Esta arquitectura no tiene protección de memoria del kernel.
Ejecutar /bin/sh como proceso init
/bin/sh: no se puede acceder a tty; control de trabajos desactivado
random: inicialización rápida completa
/ # 

Las manipulaciones anteriores nos darán un sistema huésped básico, sin elementos como /proc o un nombre de host asignado. Por ejemplo, intenta ejecutar los siguientes comandos:

- uname -av
- cat /proc/self/pid
- hostname

Para salir del sistema huésped, escribe exit o presiona control-d. Esto terminará la shell con un posterior kernel panic:

/ # exit
Kernel panic - not syncing: Attempted to kill init! exitcode=0x00000000
fish: “./linux root=/dev/root rootflag…” terminated by signal SIGABRT (Abort)

Obtuve este kernel panic porque el núcleo de Linux considera que el proceso de inicialización siempre está activo. Sin él, el sistema ya no puede funcionar y se apaga. Pero dado que se trata de un proceso en modo usuario, el resultado obtenido se envía a sí mismo en SIGABRT, lo que provoca la salida.

Configuración de la red huésped

Aquí es donde las cosas empiezan a ir mal. La red en User Mode Linux es el lugar donde toda la concepción del "modo de usuario" restringido comienza a desmoronarse. Normalmente, a nivel del sistema, la red está restringida a modos de ejecución privilegiados por razones bien entendidas.

Nota del traductor: se puede leer más sobre las diferentes opciones de trabajo con la red en UML aquí.

Viaje a slirp

Sin embargo, existe una herramienta antigua y prácticamente no soportada llamada Slirp, que permite a User Mode Linux interactuar con la red. Funciona más o menos como una pila TCP/IP a nivel de usuario y no requiere permisos del sistema para ejecutarse. Esta herramienta fue lanzada en 1995, y la última actualización fue en 2006.Slirp es muy antiguo. Con el tiempo sin soporte o actualizaciones, los compiladores han avanzado tanto que ahora esta herramienta se puede caracterizar solo como "code rot".

Así que instalemos Slirp desde los repositorios de Ubuntu y tratemos de ejecutarlo:

sudo apt-get install slirp
/usr/bin/slirp
Slirp v1.0.17 (BETA)

Copyright (c) 1995, 1996 Danny Gasparovski y otros.
Todos los derechos reservados.
Este programa está protegido por derechos de autor, software libre.
Por favor, lee el archivo COPYRIGHT que vino con el paquete Slirp para conocer los términos y condiciones del copyright.

Dirección IP del host Slirp: 127.0.0.1
Dirección IP de tu(s) DNS: 1.1.1.1, 10.77.0.7
Tu dirección es 10.0.2.15
(o cualquier otra que desees)

Escribe cinco ceros (0) para salir.

[autodetectar SLIP/CSLIP, MTU 1500, MRU 1500, 115200 baud]

SLiRP Listo...
fish: "/usr/bin/slirp" terminado por la señal SIGSEGV (error de límite de dirección)

Oh, cielos. Vamos a instalar un depurador para Slirp y veamos si podemos averiguar qué está pasando aquí:

sudo apt-get install gdb slirp-dbgsym
gdb /usr/bin/slirp
GNU gdb (Ubuntu 8.1-0ubuntu3) 8.1.0.20180409-git
Copyright (C) 2018 Free Software Foundation, Inc.
License GPLv3+: GNU GPL version 3 or later 
This is free software: you are free to change and redistribute it.
There is NO WARRANTY, to the extent permitted by law. Type "show copying"
and "show warranty" for details.
This GDB was configured as "x86_64-linux-gnu".
Type "show configuration" for configuration details.
For bug reporting instructions, please see:
.
Find the GDB manual and other documentation resources online at:
.
For help, type "help".
Type "apropos word" to search for commands related to "word"...
Reading symbols from /usr/bin/slirp...Reading symbols from /usr/lib/debug/.build-id/c6/2e75b69581a1ad85f72ac32c0d7af913d4861f.debug...done.
done.
(gdb) run
Starting program: /usr/bin/slirp
Slirp v1.0.17 (BETA)

Copyright (c) 1995,1996 Danny Gasparovski and others.
All rights reserved.
This program is copyrighted, free software.
Please read the file COPYRIGHT that came with the Slirp
package for the terms and conditions of the copyright.

IP address of Slirp host: 127.0.0.1
IP address of your DNS(s): 1.1.1.1, 10.77.0.7
Your address is 10.0.2.15
(or anything else you want)

Type five zeroes (0) to exit.

[autodetect SLIP/CSLIP, MTU 1500, MRU 1500, 115200 baud]

SLiRP Ready ...

Program received signal SIGSEGV, Segmentation fault.
                                                    ip_slowtimo () at ip_input.c:457
457     ip_input.c: No such file or directory.

El error surge en esta línea. Vamos a echar un vistazo al stacktrace, a ver si nos ayuda:

(gdb) bt full
#0  ip_slowtimo () at ip_input.c:457
        fp = 0x55784a40
#1  0x000055555556a57c in main_loop () at ./main.c:980
        so = 
        so_next = 
        timeout = {tv_sec = 0, tv_usec = 0}
        ret = 0
        nfds = 0
        ttyp = 
        ttyp2 = 
        best_time = 
        tmp_time = 
#2  0x000055555555b116 in main (argc=1, argv=0x7fffffffdc58) at ./main.c:95
No locals.

Aquí vemos que la falla ocurre al iniciar el bucle principal, cuando slirp intenta verificar los timeouts. En ese momento debería haber abandonado los intentos de depuración. Pero veamos si Slirp, compilado desde las fuentes, funciona. Volví a descargar el archivo directamente desde el sitio Sourceforge, porque cargar algo desde la línea de comando es un dolor:

cd ~/dl
wget https://xena.greedo.xeserv.us/files/slirp-1.0.16.tar.gz
tar xf slirp-1.0.16.tar.gz
cd slirp-1.0.16/src
./configure --prefix=$HOME/prefix/slirp
make

Aquí vemos alertas sobre funciones internas no definidas, es decir, la imposibilidad de enlazar el archivo binario resultante. Parece que entre 2006 y este momento GCC dejó de crear símbolos usados en funciones internas de archivos compilados intermedios. Intentemos reemplazar la palabra clave inline por un comentario vacío y ver qué pasa:

vi slirp.h
:6
a

#define inline /**/

:wq
make

No. Eso tampoco funciona. Sigue sin encontrar los símbolos de estas funciones.

En este punto me rendí y comencé a buscar en Github paquetes de compilación de Heroku. Mi teoría se basaba en que en algún paquete de compilación de Heroku estarían los archivos binarios que necesitaba. Al final, mis búsquedas me llevaron aquí. Descargué y descomprimí uml.tar.gz y encontré lo siguiente:

total 6136
-rwxr-xr-x 1 cadey cadey   79744 Dec 10  2017 ifconfig*
-rwxr-xr-x 1 cadey cadey     373 Dec 13  2017 init*
-rwxr-xr-x 1 cadey cadey  149688 Dec 10  2017 insmod*
-rwxr-xr-x 1 cadey cadey   66600 Dec 10  2017 route*
-rwxr-xr-x 1 cadey cadey  181056 Jun 26  2015 slirp*
-rwxr-xr-x 1 cadey cadey 5786592 Dec 15  2017 uml*
-rwxr-xr-x 1 cadey cadey     211 Dec 13  2017 uml_run*

¡Este es un archivo binario slirp! ¿Y funciona?

.\/slirp
Slirp v1.0.17 (BETA) FULL_BOLT

Copyright (c) 1995,1996 Danny Gasparovski y otros.
Todos los derechos reservados.
Este programa está protegido por derechos de autor, es software libre.
Por favor, lee el archivo COPYRIGHT que vino con el paquete Slirp
para conocer los términos y condiciones del copyright.

Dirección IP del host de Slirp: 127.0.0.1
Dirección IP de tu(s) DNS: 1.1.1.1, 10.77.0.7
Tu dirección es 10.0.2.15
(o cualquier otra que desees)

Escribe cinco ceros (0) para salir.

[autodetectar SLIP/CSLIP, MTU 1500, MRU 1500]

SLiRP Listo ...

No se cae, ¡así que debería funcionar! Vamos a poner este binario en ~\/bin\/slirp:

cp slirp ~\/bin\/slirp

En caso de que el creador del paquete lo elimine, yo hice un espejo.

Configuración de red

Ahora configuremos la red en nuestro núcleo invitado. Actualizaremos los parámetros de arranque:

linux 
  root=\/dev\/root 
  rootfstype=hostfs 
  rootflags=$HOME\/prefix\/uml-demo 
  rw 
  mem=64M 
  eth0=slirp,,$HOME\/bin\/slirp 
  init=\/bin\/sh

Ahora activemos la red:

mount -t proc proc proc\/\nmount -t sysfs sys sys\/\n\nifconfig eth0 10.0.2.14 netmask 255.255.255.240 broadcast 10.0.2.15
route add default gw 10.0.2.2

Los primeros dos comandos de configuración /proc y /sys son necesarios para funcionar ifconfig, que establece la interfaz de red para comunicarse con Slirp. El comando route configura la tabla de enrutamiento del núcleo para forzar que todo el tráfico pase a través del túnel Slirp. Vamos a verificar esto con una consulta DNS:

nslookup google.com 8.8.8.8
Servidor:    8.8.8.8
Dirección 1: 8.8.8.8 dns.google

Nombre:      google.com
Dirección 1: 172.217.12.206 lga25s63-in-f14.1e100.net
Dirección 2: 2607:f8b0:4006:81b::200e lga25s63-in-x0e.1e100.net

¡Funciona!

Nota: Aparentemente, la publicación original se escribió en un escritorio con una tarjeta de red por cable, o alguna otra configuración que no requiere controladores adicionales. En una laptop con WiFi 8265 de Intel, sin embargo, al activar la red, surge un error

/ # ifconfig eth0 10.0.2.14 netmask 255.255.255.240 broadcast 10.0.2.15
slirp_tramp failed - errno = 2
ifconfig: ioctl 0x8914 failed: No such file or directory
/ #

Parece que el núcleo no puede conectarse con el controlador de red. Lamentablemente, intentar compilar el firmware en el núcleo no solucionó la situación. En el momento de la publicación, no se ha encontrado una solución para esta configuración específica. En configuraciones más simples (por ejemplo, en Virtualbox), la interfaz se levanta correctamente.

Vamos a automatizar la redirección con el siguiente script de shell:

#!/bin/sh
# init.sh

mount -t proc proc proc/
mount -t sysfs sys sys/
ifconfig eth0 10.0.2.14 netmask 255.255.255.240 broadcast 10.0.2.15
route add default gw 10.0.2.2

echo "networking set up"

exec /tini /bin/sh

Y lo marcaremos como ejecutable:

chmod +x init.sh

Y luego haremos cambios en la línea de comandos del núcleo:

linux 
  root=\/dev\/root 
  rootfstype=hostfs 
  rootflags=$HOME\/prefix\/uml-demo 
  rw 
  mem=64M 
  eth0=slirp,,$HOME\/bin\/slirp 
  init=\/init.sh

Y repetimos:

SLiRP Listo ...
red configurada
\/bin\/sh: no se puede acceder a tty; el control de trabajos está desactivado

nslookup google.com 8.8.8.8
Servidor:    8.8.8.8
Dirección 1: 8.8.8.8 dns.google

Nombre:      google.com
Dirección 1: 172.217.12.206 lga25s63-in-f14.1e100.net
Dirección 2: 2607:f8b0:4004:800::200e iad30s09-in-x0e.1e100.net

¡La red funciona de manera estable!

Dockerfile

Para que te sea más fácil verificar todo esto, he reunido Dockerfile, que automatiza la mayoría de los pasos descritos y debería proporcionarte una configuración funcional. También tengo una configuración del núcleo lista, que incluye todo lo mencionado en el post. Pero es importante entender que aquí he expuesto solo la configuración mínima.

Espero que este post te haya ayudado a entender cómo levantar un núcleo invitado. Se ha convertido en un monstruo, pero la publicación estaba pensada como una guía integral sobre la construcción, instalación y configuración del Usuario Modo en Linux bajo versiones modernas de los sistemas operativos de esta familia. Los pasos posteriores deben incluir la instalación de servicios y otros software dentro del sistema invitado. Dado que las imágenes de contenedores Docker son simplemente archivos tar descomprimidos, deberías ser capaz de extraer la imagen a través de docker export, y luego definir la ruta de su instalación en la raíz del sistema de archivos del núcleo invitado. Luego, ejecuta el script de shell.

Un agradecimiento especial a Rkeene de #lobsters en Freenode. Sin su ayuda para depurar Slirp, no hubiera llegado tan lejos. No tengo ni idea de cómo su sistema Slackware funciona correctamente con slirp, pero mis sistemas Ubuntu y Alpine no aceptaron slirp ni el binario que me propuso Rkeene. Pero estoy contenta con que al menos algo funcione.

Fuente: habr.com

Compra un hosting fiable para sitios web con protección contra DDoS, servidores VPS VDS 🔥 Compra un hosting fiable para sitios web con protección contra DDoS, servidores VPS VDS | ProHoster