Introduzione del traduttore: Con l'emergere di vari tipi di contenitori nella nostra vita, può essere piuttosto interessante e utile conoscere le tecnologie da cui tutto è cominciato. Alcune di esse possono essere utilizzate ancora oggi, ma non tutti ricordano (o sanno, se non hanno assistito al loro sviluppo esplosivo). Una di queste tecnologie è User Mode Linux. L'autore dell'originale ha fatto delle ricerche approfondite per capire quali vecchie soluzioni funzionano ancora e quali no, e ha raccolto qualcosa che assomiglia a una guida passo passo su come creare un proprio UML nel 2019. E sì, abbiamo invitato su Habr l'autore del post originale. , quindi se avete domande, chiedete in inglese nei commenti.

Il User Mode in Linux è, di fatto, la porta del kernel Linux su se stesso. Questa modalità permette di eseguire un intero kernel Linux come processo utente e viene solitamente utilizzata dagli sviluppatori per testare i driver. Tuttavia, questa modalità è utile anche come strumento di isolamento generale, il cui principio è simile al funzionamento delle macchine virtuali. Questa modalità offre un isolamento maggiore rispetto a Docker, ma inferiore a quello di una macchina virtuale completa come KVM o Virtual Box.
In generale, il User Mode può sembrare uno strumento strano e complicato da utilizzare, ma ha comunque le sue aree di applicazione. È un vero e proprio kernel Linux che opera come utente non privilegiato. Questa caratteristica consente di eseguire codice potenzialmente non sicuro senza alcuna minaccia per la macchina host. Poiché si tratta di un kernel completo, i suoi processi sono isolati dalla macchina host, ovvero i processi che operano all'interno del User Mode non saranno visibili per l'host. Questo non è simile al consueto container Docker, dove la macchina host può sempre vedere i processi all'interno dello storage. Dai un'occhiata a 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 ciò con pstree del kernel Linux in User Mode:
linux─┬─5*[linux]
└─slirpQuando lavoro con contenitori Docker, posso vedere dal host i nomi dei processi in esecuzione nel sistema guest. Con il Linux User Mode questo non è possibile. Cosa significa? Significa che gli strumenti di monitoraggio che operano attraverso il sottosistema di auditing di Linux non vedono i processi eseguiti nel sistema guest. Ma in alcune situazioni questa caratteristica può diventare un'arma a doppio taglio.
In effetti, tutto il post qui sotto è una serie di ricerche e tentativi grezzi di raggiungere il risultato desiderato. Per farlo, ho dovuto usare diversi strumenti obsoleti, leggere il codice sorgente del kernel, dedicarmi a una intensa depurazione del codice scritto in un'era in cui ero ancora alle scuole elementari, e anche smanettare con le build di Heroku utilizzando un binario speciale per cercare gli strumenti che mi servivano. Tutto questo lavoro ha portato i ragazzi nel mio IRC a chiamarmi streghetta (magic). Spero che questo post possa servire a qualcuno come documentazione affidabile per realizzare la stessa cosa, ma con kernel e versioni di OS più recenti.
Impostazione
La configurazione del Linux User Mode avviene in più fasi:
- installazione delle dipendenze sull'host;
- scaricamento del kernel Linux;
- configurazione della build del kernel;
- compilazione del kernel;
- installazione del binario;
- configurazione del filesystem guest;
- selezione dei parametri di avvio del kernel;
- configurazione della rete guest;
- avvio del kernel guest.
Suppongo che se decidi di occupartene personalmente, probabilmente lo farai in una qualche distribuzione simile a Ubuntu o Debian. Ho provato a realizzare quanto descritto sopra sulla mia distribuzione preferita — Alpine, ma non ci sono riuscita, a quanto pare a causa del fatto che il kernel Linux ha un legame rigoroso con i glibc-isms per i driver in User Mode. Ho intenzione di segnalarlo all'upstream appena risolvo definitivamente il problema.
Installazione delle dipendenze sull'host
Ubuntu richiede almeno i seguenti pacchetti per compilare il kernel Linux (a condizione di un'installazione pulita):
- 'build-essential'
- 'flex'
- 'bison'
- 'xz-utils'
- 'wget'
- 'ca-certificates'
- 'bc'
- 'linux-headers'
Puoi installarli usando il seguente comando (con diritti di root o tramite sudo):
apt-get -y install build-essential flex bison xz-utils wget ca-certificates bc
linux-headers-$(uname -r) Si prega di notare che l'esecuzione del programma di configurazione del menu per il kernel Linux richiederà l'installazione di libncurses-dev. Si prega di assicurarsi che sia installato con il seguente comando (con diritti di root o tramite sudo):
apt-get -y install libncurses-devDownload del kernel
Definisci il percorso per il download e la successiva compilazione del kernel. Per questa operazione ti serviranno circa 1,3 GB di spazio su disco rigido, quindi assicurati di averlo disponibile.
Dopo, vai su e ottieni l'URL per scaricare l'ultima versione stabile del kernel. Al momento della scrittura di questo post, è:
Scarica questo file utilizzando 'wget':
wget https://cdn.kernel.org/pub/linux/kernel/v5.x/linux-5.1.16.tar.xz E estrailo con 'tar':
tar xJf linux-5.1.16.tar.xzOra accedi alla directory creata durante l'estrazione del tarball:
cd linux-5.1.16Configurazione della compilazione del kernel
Il sistema di compilazione del kernel è un insieme di con molti strumenti personalizzati e script per automatizzare il processo. Per iniziare, apri il programma interattivo di configurazione:
make ARCH=um menuconfig Questo ti guiderà parzialmente nella compilazione e ti mostrerà una finestra di dialogo. Quando in basso nella finestra apparirà '[Select]', potrai cominciare a configurare utilizzando i tasti Spazio o Invio. La navigazione nella finestra avviene come al solito con le frecce della tastiera 'su' e 'giù', mentre la selezione degli elementi può essere fatta con 'sinistra' o 'destra'.[Select]Dopo aver selezionato, puoi configurare utilizzando i tasti Spazio o Invio. La navigazione all'interno della finestra avviene normalmente con le frecce della tastiera «su» e «giù», mentre per selezionare gli elementi utilizza «sinistra» o «destra».
L'indicatore tipo —> indica che ti trovi nel sottomenu, accessibile tramite il tasto Invio. L'uscita, ovviamente, avviene attraverso ‘[Exit]‘.
Attiva le seguenti opzioni in ‘[Select]‘ e assicurati che ci sia il simbolo ‘[*]’ accanto a esse:
Opzioni specifiche UML:
- Sistema di file host
Supporto di rete (attiva questo per visualizzare il sottomenu):
- Opzioni di rete:
- Networking TCP/IP
Dispositivi di rete UML:
- Dispositivo di rete virtuale
- Trasporto SLiRP Da questa finestra, puoi uscire selezionando successivamente ‘[Exit]‘. Assicurati solo che alla fine ti venga chiesto di salvare la configurazione e scegli ‘[Yes]‘.
Ti consiglio di fare un po' di esperimenti con le opzioni di compilazione del kernel dopo aver letto questo post. Grazie a questi esperimenti, potrai apprendere molto riguardo al funzionamento delle meccaniche a basso livello del kernel e all'impatto di diversi flag sulla sua compilazione.
Compilazione del kernel
Il kernel Linux è un programma complesso che si occupa di molte cose. Anche con una configurazione minimale su hardware datato, la sua compilazione può richiedere tempo considerevole. Pertanto, compila il kernel utilizzando il seguente comando:
make ARCH=um -j$(nproc) Perché? Questo comando dirà al nostro costruttore di utilizzare tutti i core e i thread disponibili del processore durante il processo di compilazione. Il comando $(nproc) alla fine di Build sostituisce l'output del comando nproc, che fa parte di coreutils nella build standard di Ubuntu.
Dopo un po' di tempo, il nostro kernel sarà compilato in un file eseguibile ./linux.
Installazione del binario
Poiché il User Mode in Linux crea un normale binario, puoi installarlo come qualsiasi altra utility. Ecco come ho fatto:
mkdir -p ~/bin
cp linux ~/bin/linux È anche importante assicurarsi che ~/bin sia nel tuo $PATH:
export PATH=$PATH:$HOME/binImpostazione del filesystem guest
Crea una directory per il filesystem guest:
mkdir -p $HOME/prefix/uml-demo
cd $HOME/prefix
Apri alpinelinux.org e nella trova il link attuale per il download MINI ROOT FILESYSTEM. Al momento della scrittura di questo articolo, era:
http://dl-cdn.alpinelinux.org/alpine/v3.10/releases/x86_64/alpine-minirootfs-3.10.0-x86_64.tar.gzScarica 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.gzOra entra nella directory del filesystem guest ed estrai l'archivio:
cd uml-demo
tar xf ../alpine-rootfs.tgzLe azioni descritte creeranno un piccolo modello di file system. A causa delle peculiarità del sistema, sarà estremamente complicato installare i pacchetti tramite il gestore apk di Alpine. Tuttavia, questo file system sarà sufficiente per valutare l'idea generale.
Avremo anche bisogno dello strumento per fermare il consumo di memoria nel nostro kernel ospite.
wget -O tini https://github.com/krallin/tini/releases/download/v0.18.0/tini-static
chmod +x tiniCreazione di una riga di comando del kernel
Nel kernel Linux, come nella maggior parte degli altri programmi, ci sono argomenti da riga di comando di cui è possibile prendere visione indicando il tasto --help.
help
linux --help
User Mode Linux v5.1.16
disponibile su http://user-mode-linux.sourceforge.net/
--showconfig
Stampa il file di configurazione da cui è stato generato questo binario UML.
iomem=,
Configura come regione di memoria IO chiamata .
mem=
Controlla quanta memoria "fisica" il kernel alloca per il sistema. La dimensione è specificata come un numero seguito da uno di 'k', 'K', 'm', 'M', che hanno i significati ovvi. Questo non è collegato alla quantità di memoria nell'host. Può essere maggiore, e l'eccedenza, se mai utilizzata, verrà semplicemente scambiata.
Esempio: mem=64M
--help
Stampa questo messaggio.
debug
questo flag non è necessario per eseguire gdb su UML in modalità skas
root=
Questo è utilizzato dal kernel generico allo stesso modo di qualsiasi altro kernel. Se configuri un certo numero di dispositivi a blocchi e vuoi avviare qualcosa di diverso da ubd0, utilizzeresti qualcosa come:
root=/dev/ubd5
--version
Stampa il numero di versione del kernel.
umid=
Questo viene utilizzato per assegnare un'identità unica a questa macchina UML ed è utilizzato per nominare il file pid e il socket della console di gestione.
con[0-9]*=
Collega una console o una linea seriale a un canale host. Vedi http://user-mode-linux.sourceforge.net/old/input.html per una descrizione completa di questo switch.
eth[0-9]+=,
Configura un dispositivo di rete.
aio=2.4
Questo è utilizzato per costringere UML a utilizzare AIO in stile 2.4 anche quando AIO 2.6 è disponibile. L'AIO 2.4 è un singolo thread che gestisce una richiesta alla volta, in modo sincrono. L'AIO 2.6 è un thread che utilizza l'interfaccia AIO 2.6 per gestire un numero arbitrario di richieste in sospeso. L'AIO 2.6 non è disponibile in modalità tt, su host 2.4, o quando UML è costruito con /usr/include/linux/aio_abi.h non disponibile. Molte distribuzioni non includono aio_abi.h, quindi sarà necessario copiarlo da un albero del kernel nel tuo /usr/include/linux per costruire un UML capace di AIO.
nosysemu
Disattiva la patch di emulazione delle syscall per ptrace (SYSEMU). SYSEMU è una patch di prestazioni introdotta da Laurent Vivier. Cambia il comportamento di ptrace() e aiuta a ridurre i tassi di cambio di contesto dell'host. Per farlo funzionare, hai bisogno anche di una patch del kernel per il tuo host. Vedi http://perso.wanadoo.fr/laurent.vivier/UML/ per ulteriori informazioni.
uml_dir=
La posizione in cui posizionare i file pid e umid.
quiet
Disattiva i messaggi informativi durante l'avvio.
hostfs=,,...
Questo viene utilizzato per impostare i parametri di hostfs. L'argomento della directory radice viene utilizzato per limitare tutti i mount hostfs all'interno dell'albero di directory specificato sull'host. Se questo non è specificato, allora un utente all'interno di UML può montare qualsiasi cosa sull'host che è accessibile all'utente che lo esegue.
L'unico flag attualmente supportato è 'append', che specifica che tutti i file aperti da hostfs saranno aperti in modalità append.Questo testo evidenzia i parametri fondamentali di avvio. Avviamo il kernel con il set minimo di opzioni necessarie:
linux
root=/dev/root
rootfstype=hostfs
rootflags=$HOME/prefix/uml-demo
rw
mem=64M
init=/bin/shLe righe sopra comunicano al nostro kernel quanto segue:
- Supponiamo che il file system radice sia un dispositivo pseudo.
/dev/root. - Scegli come driver del file system radice.
- Monta il file system guest che abbiamo creato nel dispositivo radice.
- E sì, in modalità lettura-scrittura.
- Utilizza solo 64 megabyte di memoria (puoi usare anche meno, a seconda di cosa intendi fare, ma 64 MB sembrano un volume ottimale).
- Il kernel avvia automaticamente
/bin/shZFS archivia i dati su disco.init-processo.
Esegui questo comando e dovresti ottenere qualcosa del genere:
Un'altra riga
Limiti del core dump :
soft - 0
hard - NESSUNO
Controllo che ptrace possa cambiare i numeri delle chiamate di sistema...OK
Controllo della patch di emulazione delle chiamate di sistema per ptrace...OK
Controllo della patch avanzata di emulazione delle chiamate di sistema per ptrace...OK
Controllo delle variabili ambientali per un tempdir...nessuno trovato
Controllo se /dev/shm è su tmpfs...OK
Controllo di PROT_EXEC mmap in /dev/shm...OK
Aggiunta di 32137216 byte alla memoria fisica per colmare il gap di exec-shield
Versione Linux 5.1.16 (cadey@kahless) (gcc version 7.4.0 (Ubuntu 7.4.0-1ubuntu1~18.04.1)) #30 Domenica 7 Luglio 2019 18:57:19 UTC
Costruiti 1 zonelists, raggruppamento della mobilità attivato. Pagine totali: 23898
Linea di comando del kernel: root=/dev/root rootflags=/home/cadey/dl/uml/alpine rootfstype=hostfs rw mem=64M init=/bin/sh
Numero di voci nella tabella di hash della cache Dentry: 16384 (ordine: 5, 131072 byte)
Numero di voci nella tabella di hash della cache Inode: 8192 (ordine: 4, 65536 byte)
Memoria: 59584K/96920K disponibile (2692K codice kernel, 708K rwdata, 588K rodata, 104K init, 244K bss, 37336K riservati, 0K cma-riservati)
SLUB: HWalign=64, Ordine=0-3, MinObjects=0, CPU=1, Nodi=1
NR_IRQS: 15
clocksource: timer: mask: 0xffffffffffffffff max_cycles: 0x1cd42e205, max_idle_ns: 881590404426 ns
Calibrando il ciclo di ritardo... 7479.29 BogoMIPS (lpj=37396480)
pid_max: default: 32768 minimo: 301
Numero di voci nella tabella di hash della cache di mount: 512 (ordine: 0, 4096 byte)
Numero di voci nella tabella di hash della cache dei punti di mount: 512 (ordine: 0, 4096 byte)
Controllo che i pty dell'host supportino l'output SIGIO...Sì
Controllo che i pty dell'host supportino SIGIO alla chiusura...No, abilitazione della soluzione alternativa
devtmpfs: inizializzato
random: get_random_bytes chiamato da setup_net+0x48/0x1e0 con crng_init=0
Utilizzo di 2.6 AIO dell'host
clocksource: jiffies: mask: 0xffffffff max_cycles: 0xffffffff, max_idle_ns: 19112604462750000 ns
Numero di voci nella tabella di hash futex: 256 (ordine: 0, 6144 byte)
NET: Protocollo di famiglia 16 registrato
clocksource: passaggio a clocksource timer
NET: Protocollo di famiglia 2 registrato
tcp_listen_portaddr_hash numero di voci nella tabella di hash: 256 (ordine: 0, 4096 byte)
Numero di voci nella tabella di hash TCP established: 1024 (ordine: 1, 8192 byte)
Numero di voci nella tabella di hash TCP bind: 1024 (ordine: 1, 8192 byte)
TCP: Tabelle di hash configurate (established 1024 bind 1024)
Numero di voci nella tabella di hash UDP: 256 (ordine: 1, 8192 byte)
Numero di voci nella tabella di hash UDP-Lite: 256 (ordine: 1, 8192 byte)
NET: Protocollo di famiglia 1 registrato
console [stderr0] disabilitato
mconsole (versione 2) inizializzata su /home/cadey/.uml/tEwIjm/mconsole
Controllo del supporto MADV_REMOVE dell'host...OK
workingset: timestamp_bits=62 max_order=14 bucket_order=0
Driver bsg per SCSI (blocco generico) versione 0.4 caricato (maggiore 254)
scheduler io noop registrato (default)
scheduler io bfq registrato
loop: modulo caricato
NET: Protocollo di famiglia 17 registrato
Driver console stdio inizializzato
Utilizzando un tipo di canale configurato al di fuori di UML
setup_one_line fallito per dispositivo 1 : Configurazione fallita
Utilizzando un tipo di canale configurato al di fuori di UML
setup_one_line fallito per dispositivo 2 : Configurazione fallita
Utilizzando un tipo di canale configurato al di fuori di UML
setup_one_line fallito per dispositivo 3 : Configurazione fallita
Utilizzando un tipo di canale configurato al di fuori di UML
setup_one_line fallito per dispositivo 4 : Configurazione fallita
Utilizzando un tipo di canale configurato al di fuori di UML
setup_one_line fallito per dispositivo 5 : Configurazione fallita
Utilizzando un tipo di canale configurato al di fuori di UML
setup_one_line fallito per dispositivo 6 : Configurazione fallita
Utilizzando un tipo di canale configurato al di fuori di UML
setup_one_line fallito per dispositivo 7 : Configurazione fallita
Utilizzando un tipo di canale configurato al di fuori di UML
setup_one_line fallito per dispositivo 8 : Configurazione fallita
Utilizzando un tipo di canale configurato al di fuori di UML
setup_one_line fallito per dispositivo 9 : Configurazione fallita
Utilizzando un tipo di canale configurato al di fuori di UML
setup_one_line fallito per dispositivo 10 : Configurazione fallita
Utilizzando un tipo di canale configurato al di fuori di UML
setup_one_line fallito per dispositivo 11 : Configurazione fallita
Utilizzando un tipo di canale configurato al di fuori di UML
setup_one_line fallito per dispositivo 12 : Configurazione fallita
Utilizzando un tipo di canale configurato al di fuori di UML
setup_one_line fallito per dispositivo 13 : Configurazione fallita
Utilizzando un tipo di canale configurato al di fuori di UML
setup_one_line fallito per dispositivo 14 : Configurazione fallita
Utilizzando un tipo di canale configurato al di fuori di UML
setup_one_line fallito per dispositivo 15 : Configurazione fallita
Console inizializzata su /dev/tty0
console [tty0] abilitato
console [mc-1] abilitato
Impossibile inizializzare il dispositivo ubd 0 : Impossibile determinare la dimensione del file del dispositivo
VFS: Montato root (filesystem hostfs) sul dispositivo 0:11.
devtmpfs: montato
Questa architettura non ha protezione della memoria del kernel.
Esegui /bin/sh come processo init
/bin/sh: impossibile accedere a tty; controllo del lavoro disabilitato
random: inizializzazione veloce completata
/ # Le manipolazioni sopra ci daranno un sistema guest minimale, senza elementi come /proc o un hostname assegnato. Per esempio, prova a eseguire i seguenti comandi:
- uname -av
- cat /proc/self/pid
- hostname
Per uscire dal sistema guest, digita exit o premi control-d. Questo terminerà la shell con un successivo kernel panic:
/ # exit
Kernel panic - not syncing: Attempted to kill init! exitcode=0x00000000
fish: “./linux root=/dev/root rootflag…” terminated by signal SIGABRT (Abort) Abbiamo ottenuto questo kernel panic perché il kernel Linux considera che il processo di inizializzazione sia sempre in esecuzione. Senza di esso, il sistema non può più funzionare e si arresta. Ma poiché questo è un processo in user mode, il risultato ottenuto si invia a SIGABRT, che porta all'uscita.
Configurazione della rete guest
E qui le cose iniziano a non andare secondo i piani. La rete in User Mode Linux è il luogo in cui l'intera concezione del "modo utente" limitato inizia a crollare. Di solito, a livello di sistema, la rete è limitata da modi di esecuzione privilegiati per motivi a noi noti.
Nota editoriale: puoi leggere di più sulle diverse modalità di gestione della rete in UML .
Viaggio in slirp
Tuttavia, esiste uno strumento antico e praticamente non supportato chiamato , attraverso il quale User Mode Linux può interagire con la rete. Funziona praticamente come uno stack TCP/IP a livello utente e non richiede autorizzazioni di sistema per l'esecuzione. Questo strumento è stato rilasciato nel 1995, e l'ultima aggiornamento risale al 2006. Slirp è molto vecchio. Durante il periodo senza supporto e aggiornamenti, i compilatori sono progrediti così tanto che questo strumento può essere descritto solo come .
Quindi, installiamo Slirp dai repository Ubuntu e proviamo a eseguirlo:
sudo apt-get install slirp
/usr/bin/slirp
Slirp v1.0.17 (BETA)
Copyright (c) 1995,1996 Danny Gasparovski e altri.
Tutti i diritti riservati.
Questo programma è protetto da copyright, software gratuito.
Si prega di leggere il file COPYRIGHT che viene fornito con il pacchetto Slirp
per i termini e le condizioni del copyright.
Indirizzo IP dell'host Slirp: 127.0.0.1
Indirizzo IP dei tuoi DNS: 1.1.1.1, 10.77.0.7
Il tuo indirizzo è 10.0.2.15
(o qualsiasi altra cosa tu voglia)
Digita cinque zeri (0) per uscire.
[autodetect SLIP/CSLIP, MTU 1500, MRU 1500, 115200 baud]
SLiRP Pronto ...
fish: “/usr/bin/slirp” terminato dal segnale SIGSEGV (Errore di confine indirizzo)Oh, cielo. Installiamo un debuggere per Slirp e vediamo se possiamo capire cosa sta succedendo:
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.C'è un errore nella . Diamo un'occhiata allo stacktrace, forse ci aiuterà:
(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.Qui vediamo che il crash si verifica durante l'avvio del ciclo principale, quando slirp tenta di controllare i timeout. In quel momento avrei dovuto rinunciare ai tentativi di debug. Ma vediamo se Slirp funziona, compilato dai sorgenti. Ho scaricato nuovamente l'archivio direttamente dal sito , perché tirare qualcosa da lì tramite la riga di comando è un dolore:
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 avvertimenti su funzioni integrate non definite, cioè sull'impossibilità di linkare il file binario risultante. Sembra che nel periodo tra il 2006 e questo momento gcc abbia smesso di creare simboli utilizzati nelle funzioni integrate dei file compilati interamente. Proviamo a sostituire la parola chiave inline lascia un commento vuoto e vediamo il risultato:
vi slirp.h
:6
a
#define inline /**/
:wq
makeNo, nemmeno questo funziona. Non riesco ancora a trovare i simboli di queste funzioni.
A questo punto ho rinunciato e ho iniziato a cercare su Github . La mia teoria si basava sul fatto che in qualche pacchetto di build di Heroku ci sarebbero stati i file binari di cui avevo bisogno. Alla fine, le mie ricerche mi hanno portato . Ho scaricato e estratto 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 è un file binario slirp! E funziona?
./slirp
Slirp v1.0.17 (BETA) FULL_BOLT
Copyright (c) 1995,1996 Danny Gasparovski e altri.
Tutti i diritti riservati.
Questo programma è copyright, software libero.
Si prega di leggere il file COPYRIGHT fornito con il pacchetto Slirp
per i termini e le condizioni del copyright.
Indirizzo IP dell'host Slirp: 127.0.0.1
Indirizzo IP dei tuoi DNS: 1.1.1.1, 10.77.0.7
Il tuo indirizzo è 10.0.2.15
(o qualsiasi altra cosa tu voglia)
Digita cinque zeri (0) per uscire.
[autodetect SLIP/CSLIP, MTU 1500, MRU 1500]
SLiRP Pronto ...
Non si blocca — quindi dovrebbe funzionare! Inseriamo questo binario in ~/bin/slirp:
cp slirp ~/bin/slirpNel caso in cui il pacchetto venga eliminato dal creatore, io .
Configurazione della rete
Ora configuriamo la rete sul nostro nodo ospite. :
linux
root=/dev/root
rootfstype=hostfs
rootflags=$HOME/prefix/uml-demo
rw
mem=64M
eth0=slirp,,$HOME/bin/slirp
init=/bin/shAdesso abilitiamo 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 Le prime due comandi di configurazione /proc e /sys sono necessari per il funzionamento ifconfig, che imposta l'interfaccia di rete per comunicare con Slirp. Il comando rotta imposta la tabella di routing del kernel per forzare l'invio di tutto il traffico tramite il tunnel Slirp. Controlliamo questo con una richiesta 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.netFunziona!
Nota: A quanto pare, il post originale è stato scritto su un desktop con una scheda di rete cablata, oppure con qualche altra configurazione che non richiede driver aggiuntivi. Su un laptop con WiFi 8265 di Intel, tuttavia, si verifica un errore durante l'abilitazione 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
/ #Sembra che il kernel non riesca a connettersi al driver della scheda di rete. Purtroppo, provare a compilare il firmware nel kernel non ha risolto la situazione. Al momento della pubblicazione, non siamo riusciti a trovare soluzioni in questa configurazione. Con configurazioni più semplici (ad esempio, in Virtualbox) l'interfaccia funziona correttamente.
Automatizziamo il reindirizzamento con il seguente script 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/shE rendiamolo eseguibile:
chmod +x init.shE poi apportiamo 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 Pronto ...
networking configurato
/bin/sh: impossibile accedere a tty; controllo del lavoro disattivato
nslookup google.com 8.8.8.8
Server: 8.8.8.8
Indirizzo 1: 8.8.8.8 dns.google
Nome: google.com
Indirizzo 1: 172.217.12.206 lga25s63-in-f14.1e100.net
Indirizzo 2: 2607:f8b0:4004:800::200e iad30s09-in-x0e.1e100.netLa rete funziona in modo stabile!
Dockerfile
Per renderti più facile verificare tutto questo, ho raccolto , che automatizza la maggior parte dei passaggi descritti e dovrebbe garantirti una configurazione funzionante. Ho anche , che include tutto ciò che è descritto nel post. Ma è importante capire che qui ho esposto solo la configurazione minima.
Spero che questo post ti abbia aiutato a capire come avviare un kernel guest. È venuto fuori un vero e proprio mostro, ma l'idea era di pubblicarlo come una guida completa per l'assemblaggio, l'installazione e la configurazione della modalità utente in Linux per le versioni moderne dei sistemi operativi di questa famiglia. I passaggi successivi dovrebbero includere l'installazione dei servizi e di altro software all'interno del sistema guest. Poiché le immagini dei container Docker sono semplicemente archivi tar rilasciati, dovresti essere in grado di estrarre l'immagine tramite docker export, e poi determinare il percorso della sua installazione nella radice del file system del kernel guest. Dopodiché, esegui lo script shell.
Un ringraziamento speciale a Rkeene con #lobsters su Freenode. Senza il suo aiuto nel debug di Slirp, non sarei arrivata così lontano. Non ho idea di come il suo sistema Slackware funzioni correttamente con slirp, ma i miei sistemi Ubuntu e Alpine non hanno accettato slirp e il binario che mi ha fornito Rkeene. Ma mi accontento del fatto che qualcosa funzioni.
Fonte: habr.com
