Introduzione del traduttore: Con l'ingresso massiccio nella nostra vita di vari tipi di contenitori, potrebbe essere interessante e utile sapere da quali tecnologie tutto questo è iniziato un tempo. Alcune di esse possono essere utilizzate ancora oggi, ma non tutti ricordano (o sanno, se non erano presenti durante il loro sviluppo tumultuoso) queste modalità. Una di queste tecnologie è User Mode Linux. L'autore dell'originale ha scavato abbastanza a fondo per capire cosa delle vecchie soluzioni funzioni ancora e cosa non lo sia, raccogliendo qualcosa come una guida passo-passo su come impostare un UML domestico nel 2k19. E sì, abbiamo invitato su Habr l'autore del post originale , quindi se avete domande, chiedete in inglese nei commenti.

User Mode in Linux è, fondamentalmente, un porting del kernel Linux su se stesso. Questa modalità consente di eseguire un intero kernel Linux come processo utente e di solito viene 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. Questo modo fornisce una maggiore isolamento rispetto a Docker, ma inferiore rispetto a una macchina virtuale completa come KVM o Virtual Box.
In generale, User Mode può sembrare uno strumento strano e difficile da utilizzare, ma ha comunque le sue aree di applicazione. Infatti, si tratta di un kernel Linux completo, operante da un utente non privilegiato. Questa caratteristica consente di eseguire codice potenzialmente non sicuro senza alcuna minaccia per la macchina host. E poiché si tratta di un kernel completo, i suoi processi sono isolati dalla macchina host, ossia i processi che lavorano all'interno di User Mode non saranno visibili per l'host. Questo non è simile a un contenitore Docker tradizionale, dove la macchina host vede sempre 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 questo con pstree del kernel Linux in User Mode:
linux─┬─5*[linux]
└─slirpQuando lavoro con i container Docker, posso vedere dal host i nomi dei processi che sono in esecuzione nel sistema guest. Con Linux User Mode ciò non è possibile. Cosa significa? Significa che gli strumenti di monitoraggio che funzionano attraverso il sottosistema di auditing di Linux non vedono i processi che vengono eseguiti nel sistema guest. Ma in alcune situazioni questa caratteristica può rivelarsi un'arma a doppio taglio.
In generale, tutto il post qui sotto è un insieme di ricerche e tentativi goffi di ottenere il risultato desiderato. Per questo motivo ho dovuto usare diversi strumenti antichi, leggere il codice sorgente del kernel, fare debug intensivo del codice scritto in un'epoca in cui andavo ancora a scuola elementare e rovistare nelle build di Heroku usando un binario speciale alla ricerca degli strumenti necessari. Tutto questo lavoro ha portato al fatto che i ragazzi nel mio IRC hanno iniziato a chiamarmi maga (magic). Spero che questo post possa essere una documentazione utile per qualcuno che desideri fare esattamente la stessa cosa, ma con kernel e versioni del sistema operativo più recenti.
Configurazione
La configurazione di Linux User Mode avviene in diverse fasi:
- installazione delle dipendenze sull'host;
- download del kernel Linux;
- configurazione della build del kernel;
- compilazione del kernel;
- installazione del binario;
- configurazione del file system guest;
- scelta dei parametri di avvio del kernel;
- configurazione della rete guest;
- avvio del kernel guest.
Presumo che se decidi di gestire tutto questo da solo, molto probabilmente utilizzerai un sistema basato su Ubuntu o Debian. Ho cercato di implementare quanto sopra nel mio distribuzione preferita: Alpine, ma non ci sono riuscita, apparentemente a causa del fatto che il kernel Linux ha un legame rigido con i glibc-isms per i driver in User Mode. Ho intenzione di segnalare questo upstream una volta che avrò chiarito il problema.
Installazione delle dipendenze sull'host
Ubuntu richiede, almeno, i seguenti pacchetti per compilarne il kernel Linux (in caso di installazione pulita):
- 'build-essential'
- 'flex'
- 'bison'
- 'xz-utils'
- 'wget'
- 'ca-certificates'
- 'bc'
- 'linux-headers'
Puoi installarli utilizzando il seguente comando (con diritti di 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 configurazione del menu per il kernel Linux richiederà l'installazione di libncurses-dev. Assicurati che sia installato utilizzando il seguente comando (con diritti di root o con sudo):
apt-get -y install libncurses-devDownload del kernel
Determina il percorso per il download e la successiva compilazione del kernel. Per questa operazione, sarà necessario riservare circa 1,3 GB di spazio su disco rigido, quindi assicurati che sia disponibile.
Dopo, vai su per ottenere 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 utilizzando 'tar':
tar xJf linux-5.1.16.tar.xzOra entriamo nella 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 con una serie di strumenti e script personalizzati per automatizzare il processo. Per cominciare, apri il programma di configurazione interattiva:
make ARCH=um menuconfig Seguirà parzialmente la compilazione e ti mostrerà una finestra di dialogo. Quando nella parte inferiore della finestra compare ‘[Select]‘, potrai iniziare la configurazione usando i tasti Spazio o Invio. La navigazione nella finestra avviene, come al solito, con le frecce su e giù, e la selezione degli elementi avviene premendo le frecce sinistra o destra.
Un indicatore del tipo —> indica che sei in un sottomenu, al quale puoi accedere premendo Invio. Uscire da esso, ovviamente, mediante ‘[Exit]‘.
Attiva le seguenti opzioni in ‘[Select]‘ e assicurati che ci sia un simbolo ‘[*]’ accanto a esse:
Opzioni specifiche di UML:
- File di sistema host
Supporto di rete (attiva questo per far apparire il sottomenu):
- Opzioni di rete:
- Rete TCP/IP
Dispositivi di rete UML:
- Dispositivo di rete virtuale
- Trasporto SLiRP Da questa finestra, puoi uscire selezionando consecutivamente ‘[Exit]‘. Assicurati solo che alla fine ti sarà chiesto di salvare la configurazione e seleziona ‘[Sì]‘.
Ti consiglio di sperimentare con le opzioni di compilazione del kernel dopo aver letto questo post. Grazie a questi esperimenti, potrai apprendere molto sul funzionamento delle meccaniche di basso livello del kernel e sull'impatto di diversi flag sulla sua compilazione.
Compilazione del kernel
Il kernel Linux è un grande programma che gestisce molte cose. Anche con una configurazione minima su hardware datato, la sua compilazione può richiedere un bel po' di tempo. Pertanto, compila il kernel usando il seguente comando:
make ARCH=um -j$(nproc) Perché? Questo comando dirà al nostro compilatore di utilizzare tutti i core e i thread disponibili durante il processo di compilazione. Il comando $(nproc) alla fine della 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 proprio come qualsiasi altra utilità. Ecco come ho fatto:
mkdir -p ~/bin
cp linux ~/bin/linux Assicurati anche che ~/bin sia presente nel tuo $PATH:
export PATH=$PATH:$HOME/binConfigurazione del filesystem guest
Crea una directory per il filesystem guest:
mkdir -p $HOME/prefix/uml-demo
cd $HOME/prefix
Visita alpinelinux.org e nella cerca il link attuale per il download MINI ROOT FILESYSTEM. Al momento della scrittura di questa pubblicazione, 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 e estrai l'archivio:
cd uml-demo
tar xf ../alpine-rootfs.tgzLe azioni descritte creeranno un piccolo template di filesystem. A causa delle peculiarità del sistema, sarà estremamente difficile installare pacchetti tramite il gestore apk di Alpine. Ma questo FS sarà sufficiente per valutare l'idea generale.
Avremo anche bisogno di uno strumento per limitare il consumo di memoria del nostro kernel guest.
wget -O tini https://github.com/krallin/tini/releases/download/v0.18.0/tini-static
chmod +x tiniCreazione della riga di comando del kernel
Nel kernel di Linux, come nella maggior parte degli altri programmi, ci sono argomenti da riga di comando che possono essere consultati specificando la chiave --help.
Self --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=
Questo controlla quanta memoria "fisica" il kernel assegna
al sistema. La dimensione è specificata come un numero seguito da
uno dei 'k', 'K', 'm', 'M', i quali hanno i significati ovvi.
Questo non è correlato 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 è effettivamente utilizzato dal kernel generico nello stesso modo
in cui viene utilizzato in 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 è utilizzato per assegnare un'identità unica a questa macchina UML e
viene 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 è
disponibile AIO in 2.6. AIO 2.4 è un singolo thread che gestisce una richiesta alla
volta, in modo sincrono. AIO 2.6 è un thread che utilizza l'interfaccia AIO
di 2.6 per gestire un numero arbitrario di richieste in sospeso. 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 dovrai copiarlo da un albero del kernel a
/usr/include/linux per costruire un UML capace di AIO
nosysemu
Disattiva la patch di emulazione della chiamata di sistema 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 di una patch del kernel anche 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 è utilizzato per impostare i parametri di hostfs. L'argomento della directory root
è utilizzato per confinare tutti i mount di hostfs all'interno dell'albero di directory specificato
sull'host. Se questo non è specificato, un utente all'interno di UML può
montare qualsiasi cosa sull'host a cui ha accesso l'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 pannello illustra i principali parametri 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 dicono al nostro kernel quanto segue:
- Supponiamo che il file system radice sia un dispositivo pseudo
/dev/root. - Seleziona come driver del file system radice.
- Monta il file system guest che abbiamo creato nel dispositivo root.
- E sì, in modalità di lettura-scrittura.
- Utilizza solo 64 megabyte di memoria RAM (puoi usarne molto meno, a seconda di cosa intendi fare, ma 64 MB sembrano essere una quantità ottimale).
- Il kernel avvia automaticamente
/bin/shcomejoin-processo.
Esegui questo comando e dovresti ricevere qualcosa di simile al seguente:
Un altro documento
Limiti di 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 di emulazione avanzata delle chiamate di sistema per ptrace...OK
Controllo delle variabili di ambiente per una tempdir...nessuna trovata
Controllo se \/dev\/shm è su tmpfs...OK
Controllo PROT_EXEC mmap in \/dev\/shm...OK
Aggiunta di 32137216 byte alla memoria fisica per coprire il gap dell'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 Lug 2019 18:57:19 UTC
Costruito 1 zonelists, raggruppamento della mobilità attivo. 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
Entrate della tabella hash della cache Dentry: 16384 (ordine: 5, 131072 byte)
Entrate della tabella hash della cache Inode: 8192 (ordine: 4, 65536 byte)
Memoria: 59584K\/96920K disponibile (2692K codice del kernel, 708K rwdata, 588K rodata, 104K init, 244K bss, 37336K riservati, 0K riservati cma)
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
Entrate della tabella hash della cache di mount: 512 (ordine: 0, 4096 byte)
Entrate della tabella hash della cache di mountpoint: 512 (ordine: 0, 4096 byte)
Controllo che i ptys host supportino l'output SIGIO...Sì
Controllo che i ptys host supportino SIGIO alla chiusura...No, attivando la soluzione alternativa
devtmpfs: inizializzato
random: get_random_bytes chiamato da setup_net+0x48\/0x1e0 con crng_init=0
Utilizzando AIO host 2.6
clocksource: jiffies: mask: 0xffffffff max_cycles: 0xffffffff, max_idle_ns: 19112604462750000 ns
Entrate della tabella hash futex: 256 (ordine: 0, 6144 byte)
NET: Protocollo della famiglia 16 registrato
clocksource: Cambiato a clocksource timer
NET: Protocollo della famiglia 2 registrato
tcp_listen_portaddr_hash entrate della tabella hash: 256 (ordine: 0, 4096 byte)
Entrate della tabella hash TCP stabilite: 1024 (ordine: 1, 8192 byte)
Entrate della tabella hash TCP bind: 1024 (ordine: 1, 8192 byte)
TCP: Tabelle hash configurate (stabilite 1024 bind 1024)
Entrate della tabella hash UDP: 256 (ordine: 1, 8192 byte)
Entrate della tabella hash UDP-Lite: 256 (ordine: 1, 8192 byte)
NET: Protocollo della famiglia 1 registrato
console [stderr0] disabilitata
mconsole (versione 2) inizializzata su \/home\/cadey\/.uml\/tEwIjm\/mconsole
Controllo di supporto MADV_REMOVE host...OK
workingset: timestamp_bits=62 max_order=14 bucket_order=0
Driver SCSI generico (bsg) della layer Block versione 0.4 caricato (major 254)
scheduler I/O noop registrato (predefinito)
scheduler I/O bfq registrato
loop: modulo caricato
NET: Protocollo della famiglia 17 registrato
Inizializzato driver della console stdio
Utilizzando un tipo di canale configurato fuori da UML
setup_one_line fallito per il dispositivo 1 : Configurazione fallita
Utilizzando un tipo di canale configurato fuori da UML
setup_one_line fallito per il dispositivo 2 : Configurazione fallita
Utilizzando un tipo di canale configurato fuori da UML
setup_one_line fallito per il dispositivo 3 : Configurazione fallita
Utilizzando un tipo di canale configurato fuori da UML
setup_one_line fallito per il dispositivo 4 : Configurazione fallita
Utilizzando un tipo di canale configurato fuori da UML
setup_one_line fallito per il dispositivo 5 : Configurazione fallita
Utilizzando un tipo di canale configurato fuori da UML
setup_one_line fallito per il dispositivo 6 : Configurazione fallita
Utilizzando un tipo di canale configurato fuori da UML
setup_one_line fallito per il dispositivo 7 : Configurazione fallita
Utilizzando un tipo di canale configurato fuori da UML
setup_one_line fallito per il dispositivo 8 : Configurazione fallita
Utilizzando un tipo di canale configurato fuori da UML
setup_one_line fallito per il dispositivo 9 : Configurazione fallita
Utilizzando un tipo di canale configurato fuori da UML
setup_one_line fallito per il dispositivo 10 : Configurazione fallita
Utilizzando un tipo di canale configurato fuori da UML
setup_one_line fallito per il dispositivo 11 : Configurazione fallita
Utilizzando un tipo di canale configurato fuori da UML
setup_one_line fallito per il dispositivo 12 : Configurazione fallita
Utilizzando un tipo di canale configurato fuori da UML
setup_one_line fallito per il dispositivo 13 : Configurazione fallita
Utilizzando un tipo di canale configurato fuori da UML
setup_one_line fallito per il dispositivo 14 : Configurazione fallita
Utilizzando un tipo di canale configurato fuori da UML
setup_one_line fallito per il dispositivo 15 : Configurazione fallita
Console inizializzata su \/dev\/tty0
console [tty0] abilitata
console [mc-1] abilitata
Impossibile inizializzare il dispositivo ubd 0 : Impossibile determinare la dimensione del file del dispositivo
VFS: Root montato (filesystem hostfs) sul dispositivo 0:11.
devtmpfs: montato
Questa architettura non ha protezione della memoria del kernel.
Esegui \/bin\/sh come processo di init
\/bin\/sh: impossibile accedere al tty; controllo lavoro disattivato
random: fast init completato
\ # Le manipolazioni sopra ci daranno un sistema guest in versione ridotta, senza cose come /proc o nome host assegnato. Per esempio, prova a eseguire i seguenti comandi:
- uname -av
- cat /proc/self/pid
- hostname
Per uscire dal sistema guest, inserisci exit o premi control-d. Questo interromperà 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 ricevuto questo kernel panic perché il kernel di Linux ritiene che il processo di inizializzazione sia sempre avviato. Senza di esso, il sistema non può più funzionare e termina. Ma poiché si tratta di un processo in modalità utente, il risultato ottenuto si invia a SIGABRT, che porta all'uscita.
Configurazione della rete guest
Qui le cose iniziano a non andare come previsto. La rete in User Mode Linux è il punto in cui l'intera concezione della "modalità utente" limitata inizia a crollare. Infatti, di solito a livello di sistema la rete è limitata da modalità privilegiate per motivi a noi ben noti.
Nota dell'editore: puoi leggere di più sui diversi modi di lavorare con la rete in UML .
Un viaggio in slirp
Tuttavia, esiste un antico strumento praticamente non supportato chiamato , tramite cui User Mode Linux può interagire con la rete. Funziona più o meno come un stack TCP/IP a livello utente e non richiede alcun permesso di sistema per l'esecuzione. Questo strumento è stato rilasciato nel 1995, e l'ultimo aggiornamento risale a 2006. Slirp è molto vecchio. In tutto questo tempo senza supporto e aggiornamenti, i compilatori sono progrediti così tanto che ora questo strumento può essere descritto solo come .
Quindi, installiamo Slirp dai repository di Ubuntu e proviamo a farlo partire:
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 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 del tuo DNS: 1.1.1.1, 10.77.0.7
Il tuo indirizzo è 10.0.2.15
(o qualsiasi altro tu voglia)
Scrivi cinque zero (0) per uscire.
[autodetect SLIP/CSLIP, MTU 1500, MRU 1500, 115200 baud]
SLiRP Pronto ...
fishing: "/usr/bin/slirp" terminato dal segnale SIGSEGV (Errore di confine dell'indirizzo)Oh, cielo. Installiamo un debugger per Slirp e vediamo se riusciamo a 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.
Licenza GPLv3+: GNU GPL versione 3 o successiva
Questo è software gratuito: sei libero di modificarlo e redistribuirlo.
Non c'è GARANZIA, nella misura consentita dalla legge. Digita "mostra copia"
e "mostra garanzia" per i dettagli.
Questo GDB è stato configurato come "x86_64-linux-gnu".
Digita "mostra configurazione" per i dettagli di configurazione.
Per le istruzioni sulla segnalazione dei bug, vedi:
.
Trova il manuale GDB e altre risorse di documentazione online su:
.
Per aiuto, digita "aiuto".
Digita "apropos parola" per cercare comandi correlati a "parola"...
Lettura dei simboli da /usr/bin/slirp...Lettura dei simboli da /usr/lib/debug/.build-id/c6/2e75b69581a1ad85f72ac32c0d7af913d4861f.debug...fatto.
fatto.
(gdb) run
Avvio programma: /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.
Leggi il file COPYRIGHT che è venuto con il pacchetto Slirp
per i termini e le condizioni del copyright.
Indirizzo IP dell'host Slirp: 127.0.0.1
Indirizzi 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 ...
Il programma ha ricevuto il segnale SIGSEGV, errore di segmentazione.
ip_slowtimo () at ip_input.c:457
457 ip_input.c: File o directory non esistente.L'errore si verifica nella . Diamo un'occhiata allo stacktrace, potrebbe aiutarci con qualcosa:
(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
Nessuna variabile locale.Qui vediamo che il crash si verifica durante l'avvio del ciclo principale, quando slirp tenta di controllare i timeout. A questo punto avrei dovuto rinunciare ai tentativi di debug. Ma vediamo se Slirp, costruito dai sorgenti, funziona. Ho scaricato nuovamente l'archivio direttamente dal sito , perché prelevare 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 avvisi su funzioni integrate non definite, cioè sull'impossibilità di linkare il file binario risultante. Sembra che tra il 2006 e adesso, gcc abbia smesso di generare simboli usati nelle funzioni integrate dei file compilati in modo intermedio. Proviamo a sostituire la parola chiave inline con un commento vuoto e vediamo il risultato:
vi slirp.h
:6
a
#define inline /**/
:wq
makeNo, nemmeno questo funziona. Ancora non riesco a trovare i caratteri di queste funzioni.
A questo punto mi sono arresa 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 ricerche mi hanno portata . 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! Ma funziona?
./slirp
Slirp v1.0.17 (BETA) FULL_BOLT
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 è venuto 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 arresta, quindi dovrebbe funzionare! Mettiamo questo binario in ~/bin/slirp:
cp slirp ~/bin/slirpNel caso in cui il creatore del pacchetto lo rimuova, ho .
Configurazione della rete
Ora configuriamo la rete sul nostro kernel ospite. :
linux
root=/dev/root
rootfstype=hostfs
rootflags=$HOME/prefix/uml-demo
rw
mem=64M
eth0=slirp,,$HOME/bin/slirp
init=/bin/shOra attiviamo 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 far funzionare ifconfig, che imposta l'interfaccia di rete per comunicare con Slirp. Il comando route imposta la tabella di routing del kernel forzando l'invio di tutto il traffico attraverso il tunnel Slirp. Controlliamo questo con una richiesta DNS:
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: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 qualche altra configurazione che non richiede driver aggiuntivi. Su un laptop con WiFi 8265 di Intel però, si verifica un errore quando si attiva la 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 di rete. Purtroppo, l'implementazione del firmware nel kernel non ha risolto la situazione. Al momento della pubblicazione, non siamo riusciti a trovare soluzioni in questa configurazione. In configurazioni più semplici (ad esempio, in Virtualbox), l'interfaccia si attiva correttamente.
Automatizziamo il reindirizzamento utilizzando 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 ...
rete impostata
\/bin\/sh: impossibile accedere a tty; controllo del job 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 facilitarvi la verifica, ho raccolto , che automatizza la maggior parte dei passaggi descritti e dovrebbe fornirvi 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 vi abbia aiutato a comprendere come avviare un kernel ospite. È venuto fuori un certo mostro, ma l'intento di questa pubblicazione era quello di servire come guida completa sulla compilazione, installazione e configurazione dell'User Mode in Linux con le versioni moderne dei sistemi operativi di questa famiglia. Le azioni successive dovrebbero includere l'installazione dei servizi e di altro software all'interno del sistema ospite. Poiché le immagini dei contenitori Docker sono semplicemente archivi tar decompressi, dovreste essere in grado di estrarre l'immagine attraverso docker export, e poi specificare il percorso della sua installazione nella radice del filesystem del kernel ospite. Infine, eseguire lo script shell.
Un ringraziamento speciale a Rkeene su #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 suggerito Rkeene. Ma mi accontento del fatto che qualcosa funzioni.
Fonte: habr.com
