Come iniziare a utilizzare la modalità utente in Linux

Introduzione del traduttore: Considerando il massiccio ingresso nelle nostre vite di contenitori di vario genere, può essere molto interessante e utile scoprire con quali tecnologie tutto è iniziato una volta. Alcuni di essi possono essere utilizzati utilmente fino ad oggi, ma non tutti ricordano tali metodi (o sanno se non sono stati colti durante il loro rapido sviluppo). Una di queste tecnologie è User Mode Linux. L'autore dell'originale ha scavato molto, cercando di capire quali dei vecchi sviluppi funzionano ancora e quali no, e ha messo insieme qualcosa come un'istruzione passo passo su come procurarsi un UML fatto in casa in 2k19. E sì, abbiamo invitato l'autore del post originale su Habr Cadey, quindi se hai domande, chiedi in inglese nei commenti.

Come iniziare a utilizzare la modalità utente in Linux

La modalità utente in Linux è, infatti, un port del kernel Linux su se stesso. Questa modalità consente di eseguire un kernel Linux completo come processo utente ed è comunemente utilizzata dagli sviluppatori per testare i driver. Ma questa modalità è utile anche come strumento di isolamento generale, il cui principio è simile al funzionamento delle macchine virtuali. Questa modalità fornisce un maggiore isolamento rispetto a Docker, ma meno di una macchina virtuale a tutti gli effetti come KVM o Virtual Box.

In generale, la Modalità Utente può sembrare uno strumento strano e difficile da usare, ma ha comunque la sua utilità. Dopotutto, questo è un kernel Linux completo eseguito da un utente non privilegiato. Questa funzionalità consente l'esecuzione di codice potenzialmente non attendibile senza alcuna minaccia per il computer host. E poiché si tratta di un kernel a tutti gli effetti, i suoi processi sono isolati dalla macchina host i processi in esecuzione in modalità utente non saranno visibili all'host. Questo non è come il solito contenitore Docker, nel qual caso la macchina host vede sempre i processi all'interno del repository. Guarda questo pezzo di pstree da uno dei miei server:

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}]

E confronta questo con il pstree del kernel Linux in modalità utente:

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

Quando lavoro con i contenitori Docker, posso vedere dall'host i nomi dei processi in esecuzione nel guest. Con la modalità utente Linux, questo non è possibile. Cosa significa? Ciò significa che gli strumenti di monitoraggio che vengono eseguiti attraverso il sottosistema di controllo di Linux non vedere processi in esecuzione nel sistema ospite. Ma in alcune situazioni, questa caratteristica può diventare un’arma a doppio taglio.

In generale, l'intero post seguente è una raccolta di ricerche e tentativi approssimativi per ottenere il risultato desiderato. Per fare questo, ho dovuto utilizzare vari strumenti antichi, leggere i sorgenti del kernel, eseguire un debug intenso del codice scritto ai tempi in cui ero ancora alle elementari e anche armeggiare con le build di Heroku utilizzando uno speciale binario per trovare gli strumenti di cui avevo bisogno . Tutto questo lavoro ha portato i ragazzi del mio IRC a chiamarmi magico. Spero che questo post serva come documentazione affidabile per qualcuno che possa provare la stessa cosa con kernel e versioni del sistema operativo più recenti.

registrazione

La configurazione della modalità utente Linux viene eseguita in diversi passaggi:

  • installazione delle dipendenze sull'host;
  • scaricare il kernel Linux;
  • configurazione della compilazione del kernel;
  • assemblaggio del nocciolo;
  • installazione binaria;
  • configurazione del file system ospite;
  • selezione dei parametri di avvio del kernel;
  • creazione di una rete ospite;
  • avviando il kernel guest.

Presumo che se decidi di farlo da solo, molto probabilmente farai tutto ciò che è descritto in qualche sistema simile a Ubuntu o Debian. Ho provato a implementare tutto quanto sopra nella mia distribuzione preferita, Alpine, ma non ne è venuto fuori nulla, apparentemente a causa del fatto che il kernel Linux ha un glibc-isms vincolante per i driver in modalità utente. Ho intenzione di segnalare questo a monte dopo aver finalmente compreso il problema.

Installazione delle dipendenze sull'host

Ubuntu richiede almeno i seguenti pacchetti per creare il kernel Linux (assumendo un'installazione pulita):

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

Puoi installarli con il seguente comando (come root o con sudo):

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

Tieni presente che l'esecuzione del programma di installazione del menu del kernel Linux richiederà l'installazione di libncurses-dev. Assicurati che sia installato con il seguente comando (come root o con sudo):

apt-get -y install libncurses-dev

Scaricamento del kernel

Decidi dove scaricare e poi costruisci il kernel. Per questa operazione dovrai allocare circa 1,3 GB di spazio su disco rigido, quindi assicurati di averlo.

Dopo vai a kernel.org e ottieni l'URL per scaricare l'ultimo kernel stabile. Al momento in cui scrivo questo è: https://cdn.kernel.org/pub/linux/kernel/v5.x/linux-5.1.16.tar.xz

Scarica questo file utilizzando 'wget':

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

Ed estrailo con 'tar':

tar xJf linux-5.1.16.tar.xz

Ora entriamo nella directory creata durante la decompressione del tarball:

cd linux-5.1.16

Configurazione della compilazione del kernel

Il sistema di compilazione del kernel è un set Makefile с molti strumenti e script personalizzati per automatizzare il processo. Innanzitutto, apri il programma di installazione interattivo:

make ARCH=um menuconfig

Costruirà e visualizzerà parzialmente una finestra di dialogo per te. Quando '[Select]', potrai effettuare la configurazione utilizzando i tasti Spazio o Invio. Naviga nella finestra, come al solito, con le frecce della tastiera "su" e "giù" e seleziona gli elementi "sinistra" o "destra".

Il puntatore di visualizzazione —> significa che ci si trova in un sottomenu, accessibile tramite il tasto Invio. La via d'uscita è ovviamente attraverso '[Exit]'.

Includi le seguenti opzioni in "[Select]' e assicurati che abbiano un '[*]' accanto a loro:

UML-specific Options:
  - Host filesystem
Networking support (enable this to get the submenu to show up):
  - Networking options:
    - TCP/IP Networking
UML Network devices:
  - Virtual network device
  - SLiRP transport

Fatto, potete uscire da questa finestra selezionando successivamente '[Exit]'. Assicurati solo che ti venga chiesto di salvare la configurazione alla fine e seleziona "[Yes]'.

Ti consiglio di giocare con le opzioni di compilazione del kernel dopo aver letto questo post. Attraverso questi esperimenti, puoi imparare molto in termini di comprensione del lavoro della meccanica del kernel di basso livello e dell'impatto di vari flag sul suo assemblaggio.

Costruire il kernel

Il kernel Linux è un grande programma che fa molte cose. Anche con una configurazione così minima su un vecchio hardware, la creazione può richiedere parecchio tempo. Quindi costruisci il kernel con il seguente comando:

make ARCH=um -j$(nproc)

Per quello? Questo comando dirà al nostro builder di utilizzare tutti i core e thread della CPU disponibili nel processo di compilazione. Squadra $(nproc) alla fine di Build sostituisce l'output del comando nproc, di cui fa parte coreutils in una build Ubuntu standard.

Dopo un po' di tempo, il nostro kernel verrà compilato in un file eseguibile ./linux.

Installazione del binario

Poiché la modalità utente in Linux crea un file binario regolare, puoi installarlo come qualsiasi altra utility. Ecco come l'ho fatto:

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

Vale anche la pena accertarsene ~/bin è nel tuo $PATH:

export PATH=$PATH:$HOME/bin

Configurazione del file system guest

Crea una directory per il file system guest:

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

Apri alpinelinux.org e accedi sezione download trovare il collegamento per il download effettivo MINI ROOT FILESYSTEM. Al momento in cui scrivo questo era:

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

Scarica questo tarball usando 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

Ora entriamo nella directory del file system guest ed estraiamo l'archivio:

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

Le azioni descritte creeranno un piccolo modello di file system. A causa della natura del sistema, sarà estremamente difficile installare i pacchetti tramite il gestore apk Alpine. Ma questo FS sarà sufficiente per valutare l'idea generale.

Abbiamo anche bisogno di uno strumento tini per limitare il consumo di memoria processi zombie il nostro kernel ospite.

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

Creazione di una riga di comando del kernel

Il kernel Linux, come la maggior parte degli altri programmi, ha argomenti della riga di comando a cui è possibile accedere specificando la chiave --help.

Lui stesso: aiuto

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

--showconfig
    Prints the config file that this UML binary was generated from.

iomem=<name>,<file>
    Configure <file> as an IO memory region named <name>.

mem=<Amount of desired ram>
    This controls how much "physical" memory the kernel allocates
    for the system. The size is specified as a number followed by
    one of 'k', 'K', 'm', 'M', which have the obvious meanings.
    This is not related to the amount of memory in the host.  It can
    be more, and the excess, if it's ever used, will just be swapped out.
        Example: mem=64M

--help
    Prints this message.

debug
    this flag is not needed to run gdb on UML in skas mode

root=<file containing the root fs>
    This is actually used by the generic kernel in exactly the same
    way as in any other kernel. If you configure a number of block
    devices and want to boot off something other than ubd0, you
    would use something like:
        root=/dev/ubd5

--version
    Prints the version number of the kernel.

umid=<name>
    This is used to assign a unique identity to this UML machine and
    is used for naming the pid file and management console socket.

con[0-9]*=<channel description>
    Attach a console or serial line to a host channel.  See
    http://user-mode-linux.sourceforge.net/old/input.html for a complete
    description of this switch.

eth[0-9]+=<transport>,<options>
    Configure a network device.
    
aio=2.4
    This is used to force UML to use 2.4-style AIO even when 2.6 AIO is
    available.  2.4 AIO is a single thread that handles one request at a
    time, synchronously.  2.6 AIO is a thread which uses the 2.6 AIO
    interface to handle an arbitrary number of pending requests.  2.6 AIO
    is not available in tt mode, on 2.4 hosts, or when UML is built with
    /usr/include/linux/aio_abi.h not available.  Many distributions don't
    include aio_abi.h, so you will need to copy it from a kernel tree to
    your /usr/include/linux in order to build an AIO-capable UML

nosysemu
    Turns off syscall emulation patch for ptrace (SYSEMU).
    SYSEMU is a performance-patch introduced by Laurent Vivier. It changes
    behaviour of ptrace() and helps reduce host context switch rates.
    To make it work, you need a kernel patch for your host, too.
    See http://perso.wanadoo.fr/laurent.vivier/UML/ for further
    information.

uml_dir=<directory>
    The location to place the pid and umid files.

quiet
    Turns off information messages during boot.

hostfs=<root dir>,<flags>,...
    This is used to set hostfs parameters.  The root directory argument
    is used to confine all hostfs mounts to within the specified directory
    tree on the host.  If this isn't specified, then a user inside UML can
    mount anything on the host that's accessible to the user that's running
    it.
    The only flag currently supported is 'append', which specifies that all
    files opened by hostfs will be opened in append mode.

Questo pannello evidenzia i principali parametri del lancio. Eseguiamo il kernel con il set minimo di opzioni richiesto:

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

Le righe sopra dicono al nostro kernel quanto segue:

  • Supponiamo che il filesystem root sia uno pseudo dispositivo /dev/root.
  • Scegliere hostfs come driver del file system root.
  • Monta il filesystem guest che abbiamo creato sul dispositivo root.
  • E sì, in modalità lettura-scrittura.
  • Usa solo 64 MB di RAM (puoi usarne molto meno a seconda di cosa intendi fare, ma 64 MB sembra la quantità ottimale).
  • Il kernel si avvia automaticamente /bin/sh come init-processi.

Esegui questo comando e dovresti ottenere qualcosa di simile al seguente:

Un altro foglio

Core dump limits :
        soft - 0
        hard - NONE
Checking that ptrace can change system call numbers...OK
Checking syscall emulation patch for ptrace...OK
Checking advanced syscall emulation patch for ptrace...OK
Checking environment variables for a tempdir...none found
Checking if /dev/shm is on tmpfs...OK
Checking PROT_EXEC mmap in /dev/shm...OK
Adding 32137216 bytes to physical memory to account for exec-shield gap
Linux version 5.1.16 (cadey@kahless) (gcc version 7.4.0 (Ubuntu 7.4.0-1ubuntu1~18.04.1)) #30 Sun Jul 7 18:57:19 UTC 2019
Built 1 zonelists, mobility grouping on.  Total pages: 23898
Kernel command line: root=/dev/root rootflags=/home/cadey/dl/uml/alpine rootfstype=hostfs rw mem=64M init=/bin/sh
Dentry cache hash table entries: 16384 (order: 5, 131072 bytes)
Inode-cache hash table entries: 8192 (order: 4, 65536 bytes)
Memory: 59584K/96920K available (2692K kernel code, 708K rwdata, 588K rodata, 104K init, 244K bss, 37336K reserved, 0K cma-reserved)
SLUB: HWalign=64, Order=0-3, MinObjects=0, CPUs=1, Nodes=1
NR_IRQS: 15
clocksource: timer: mask: 0xffffffffffffffff max_cycles: 0x1cd42e205, max_idle_ns: 881590404426 ns
Calibrating delay loop... 7479.29 BogoMIPS (lpj=37396480)
pid_max: default: 32768 minimum: 301
Mount-cache hash table entries: 512 (order: 0, 4096 bytes)
Mountpoint-cache hash table entries: 512 (order: 0, 4096 bytes)
Checking that host ptys support output SIGIO...Yes
Checking that host ptys support SIGIO on close...No, enabling workaround
devtmpfs: initialized
random: get_random_bytes called from setup_net+0x48/0x1e0 with crng_init=0
Using 2.6 host AIO
clocksource: jiffies: mask: 0xffffffff max_cycles: 0xffffffff, max_idle_ns: 19112604462750000 ns
futex hash table entries: 256 (order: 0, 6144 bytes)
NET: Registered protocol family 16
clocksource: Switched to clocksource timer
NET: Registered protocol family 2
tcp_listen_portaddr_hash hash table entries: 256 (order: 0, 4096 bytes)
TCP established hash table entries: 1024 (order: 1, 8192 bytes)
TCP bind hash table entries: 1024 (order: 1, 8192 bytes)
TCP: Hash tables configured (established 1024 bind 1024)
UDP hash table entries: 256 (order: 1, 8192 bytes)
UDP-Lite hash table entries: 256 (order: 1, 8192 bytes)
NET: Registered protocol family 1
console [stderr0] disabled
mconsole (version 2) initialized on /home/cadey/.uml/tEwIjm/mconsole
Checking host MADV_REMOVE support...OK
workingset: timestamp_bits=62 max_order=14 bucket_order=0
Block layer SCSI generic (bsg) driver version 0.4 loaded (major 254)
io scheduler noop registered (default)
io scheduler bfq registered
loop: module loaded
NET: Registered protocol family 17
Initialized stdio console driver
Using a channel type which is configured out of UML
setup_one_line failed for device 1 : Configuration failed
Using a channel type which is configured out of UML
setup_one_line failed for device 2 : Configuration failed
Using a channel type which is configured out of UML
setup_one_line failed for device 3 : Configuration failed
Using a channel type which is configured out of UML
setup_one_line failed for device 4 : Configuration failed
Using a channel type which is configured out of UML
setup_one_line failed for device 5 : Configuration failed
Using a channel type which is configured out of UML
setup_one_line failed for device 6 : Configuration failed
Using a channel type which is configured out of UML
setup_one_line failed for device 7 : Configuration failed
Using a channel type which is configured out of UML
setup_one_line failed for device 8 : Configuration failed
Using a channel type which is configured out of UML
setup_one_line failed for device 9 : Configuration failed
Using a channel type which is configured out of UML
setup_one_line failed for device 10 : Configuration failed
Using a channel type which is configured out of UML
setup_one_line failed for device 11 : Configuration failed
Using a channel type which is configured out of UML
setup_one_line failed for device 12 : Configuration failed
Using a channel type which is configured out of UML
setup_one_line failed for device 13 : Configuration failed
Using a channel type which is configured out of UML
setup_one_line failed for device 14 : Configuration failed
Using a channel type which is configured out of UML
setup_one_line failed for device 15 : Configuration failed
Console initialized on /dev/tty0
console [tty0] enabled
console [mc-1] enabled
Failed to initialize ubd device 0 :Couldn't determine size of device's file
VFS: Mounted root (hostfs filesystem) on device 0:11.
devtmpfs: mounted
This architecture does not have kernel memory protection.
Run /bin/sh as init process
/bin/sh: can't access tty; job control turned off
random: fast init done
/ # 

Le manipolazioni di cui sopra ci daranno sistema ospite al minimo, senza cose come /proc o il nome host assegnato. Ad esempio, prova i seguenti comandi:

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

Per disconnettersi dall'ospite, digitare exit oppure premi control-d. Questo ucciderà la shell seguito da un panico del kernel:

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

Abbiamo avuto questo panico nel kernel perché il kernel Linux pensa che il processo di inizializzazione sia sempre in esecuzione. Senza di esso, il sistema non può più funzionare e si blocca. Ma poiché si tratta di un processo in modalità utente, l'output risultante si invia a SIGABRT, che si traduce in un output.

Configurazione della rete ospite

Ed è qui che le cose cominciano ad andare storte. Networking in modalità utente Linux è il luogo in cui l'intero concetto di "modalità utente" limitata inizia a cadere a pezzi. Dopotutto, di solito a livello di sistema, la rete è limitata privilegiato modalità di esecuzione per ragioni tutti comprensibili.

Nota. per .: puoi leggere di più sulle diverse opzioni per lavorare con una rete in UML qui.

Viaggio a Slirp

Tuttavia, esiste uno strumento antico e quasi non supportato chiamato scivolare, con la quale la modalità utente Linux può interagire con la rete. Funziona in modo molto simile a uno stack TCP/IP a livello utente e non richiede alcuna autorizzazione di sistema per essere eseguito. Questo strumento era uscito nel 1995, e l'ultimo aggiornamento è datato Anno 2006. Slipr è molto vecchio. Nel corso del tempo senza supporto e aggiornamenti, i compilatori sono arrivati ​​​​al punto che ora questo strumento può essere descritto solo come marciume del codice.

Scarichiamo quindi Slirp dai repository di Ubuntu e proviamo a eseguirlo:

sudo apt-get install slirp
/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 ...
fish: “/usr/bin/slirp” terminated by signal SIGSEGV (Address boundary error)

Oh dei. Installiamo il debugger di Slirp e vediamo se riusciamo a capire cosa sta succedendo qui:

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 <http://gnu.org/licenses/gpl.html>
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:
<http://www.gnu.org/software/gdb/bugs/>.
Find the GDB manual and other documentation resources online at:
<http://www.gnu.org/software/gdb/documentation/>.
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.

L'errore batte in noi questa linea. Diamo un'occhiata allo stacktrace, forse qualcosa ci aiuterà lì:

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

Qui vediamo che l'arresto anomalo avviene durante l'avvio del ciclo principale quando slipr tenta di verificare i timeout. A questo punto, ho dovuto rinunciare a provare a eseguire il debug. Ma vediamo se Slirp costruito in questo modo funziona. Ho riscaricato l'archivio direttamente dal sito Sourceforge, perché trascinare qualcosa da lì attraverso la riga di comando è una seccatura:

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

Qui vediamo avvisi su funzioni integrate non definite, ovvero sull'impossibilità di collegare il file binario risultante. Sembra che tra il 2006 e questo punto, gcc abbia smesso di produrre simboli utilizzati nelle funzioni integrate dei file compilati intermedi. Proviamo a sostituire la parola chiave inline su un commento vuoto e guarda il risultato:

vi slirp.h
:6
a
<enter>
#define inline /**/
<escape>
:wq
make

No. Anche questo non funziona. Ancora non riesco a trovare i simboli per queste funzioni.

A questo punto ho rinunciato e ho iniziato a cercare su Github Pacchetti di creazione di Heroku. La mia teoria era che qualche pacchetto di build di Heroku contenesse i binari di cui avevo bisogno. Alla fine, la ricerca mi ha portato qui. Ho scaricato e scompattato uml.tar.gz e ho trovato quanto segue:

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*

Questo è il binario sliprp! Lui lavora?

./slirp
Slirp v1.0.17 (BETA) FULL_BOLT

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]

SLiRP Ready ...

Non si blocca, quindi dovrebbe funzionare! Inseriamo questo binario ~/bin/slirp:

cp slirp ~/bin/slirp

Nel caso in cui il creatore del pacchetto lo rimuova, I fatto uno specchio.

Configurazione di rete

Ora impostiamo la rete sul nostro kernel ospite. Aggiorna le opzioni di avvio:

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

Ora accendiamo la rete:

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

I primi due comandi di configurazione /proc и /sys necessari per il lavoro ifconfig, che imposta l'interfaccia di rete per comunicare con Slirp. Squadra route imposta la tabella di routing del kernel per forzare l'invio di tutto il traffico attraverso il tunnel Slirp. Verifichiamolo con una query DNS:

nslookup google.com 8.8.8.8
Server:    8.8.8.8
Address 1: 8.8.8.8 dns.google

Name:      google.com
Address 1: 172.217.12.206 lga25s63-in-f14.1e100.net
Address 2: 2607:f8b0:4006:81b::200e lga25s63-in-x0e.1e100.net

Работает!

Nota per.: A quanto pare, il post originale è stato scritto su un desktop con una scheda di rete cablata o qualche altra configurazione che non richiedeva driver aggiuntivi. Su un laptop con WiFi 8265 di Intel, si verifica un errore durante l'avvio della rete

/ # 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
/ #

Apparentemente il kernel non riesce a comunicare con il driver della scheda di rete. Un tentativo di compilare il firmware nel kernel, sfortunatamente, non ha risolto la situazione. Al momento della pubblicazione non è stato possibile trovare una soluzione in questa configurazione. Nelle configurazioni più semplici (ad esempio in Virtualbox), l'interfaccia si alza correttamente.

Automatizziamo il reindirizzamento con il seguente script di 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

E contrassegnalo come eseguibile:

chmod +x init.sh

E poi apporteremo modifiche alla riga di comando del kernel:

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

E ripetiamo:

SLiRP Ready ...
networking set up
/bin/sh: can't access tty; job control turned off

nslookup google.com 8.8.8.8
Server:    8.8.8.8
Address 1: 8.8.8.8 dns.google

Name:      google.com
Address 1: 172.217.12.206 lga25s63-in-f14.1e100.net
Address 2: 2607:f8b0:4004:800::200e iad30s09-in-x0e.1e100.net

La rete è stabile!

file della finestra mobile

Per facilitarti il ​​controllo di tutto questo, ho raccolto Dockerfile, che automatizza la maggior parte dei passaggi descritti e dovrebbe fornire una configurazione funzionante. Ho anche kernel preconfigurato, che ha tutto ciò che è descritto nel post. Ma è importante capire che qui ho delineato solo l'impostazione minima.

Spero che questo post ti abbia aiutato a capire come generare un kernel guest. Si è rivelato una specie di mostro, ma la pubblicazione è stata concepita come una guida completa sulla creazione, installazione e configurazione della modalità utente in Linux nelle versioni moderne dei sistemi operativi di questa famiglia. I passaggi successivi dovrebbero includere l'installazione di servizi e altro software già all'interno del sistema ospite. Poiché le immagini del contenitore Docker sono solo tarball pubblicizzati, dovresti essere in grado di estrarre l'immagine tramite docker export, quindi determinarne il percorso di installazione nella radice del file system del kernel guest. Bene, allora esegui lo script di shell.

Un ringraziamento speciale a Rkeene di #lobsters su Freenode. Senza il suo aiuto nel debug di Slirp, non sarei arrivato fin qui. Non ho idea di come il suo sistema Slackware funzioni correttamente con slirp, ma i miei sistemi Ubuntu e Alpine non accettavano slirp e il binario suggerito da Rkeene. Ma mi basta che almeno qualcosa funzioni per me.

Fonte: habr.com

Aggiungi un commento