Introducere de la traducător: Pe fondul pătrunderii în viața noastră a diferitelor tipuri de containere, poate fi interesant și util să aflăm de la ce tehnologii a început totul cândva. Unele dintre acestea pot fi utilizate încă și astăzi, dar nu toată lumea își amintește de aceste metode (sau nu știe, dacă nu a trăit perioada lor de dezvoltare intensă). Una dintre aceste tehnologii este User Mode Linux. Autorul original a cercetat îndeaproape ce dintre vechile realizări mai funcționează și ce nu mai este la fel de relevant și a adunat o serie de instrucțiuni pas cu pas despre cum să-ți creezi propriul UML în 2019. Și da, l-am invitat pe Habr pe autorul postului original , așa că dacă aveți întrebări — întrebați în engleză în comentarii.

User Mode în Linux — este, practic, un port al nucleului Linux pe sine însuși. Acest mod permite rularea unui nucleu Linux complet ca un proces utilizator și este folosit de obicei de dezvoltatori pentru testarea driverelor. Dar acest mod este de asemenea util ca un instrument de izolare generală, principiu care se aseamănă cu funcționarea mașinilor virtuale. Acest mod oferă o izolare mai mare decât Docker, dar mai puțină decât o mașină virtuală completă, cum ar fi KVM sau Virtual Box.
În general, User Mode poate părea un instrument ciudat și complicat de utilizat, dar are totuși domeniile sale de aplicare. Este un nucleu Linux complet, care funcționează de la un utilizator fără privilegii. Această caracteristică permite rularea de cod potențial nesigur fără nicio amenințare pentru mașina gazdă. Și, fiind un nucleu complet, procesele sale sunt izolate de mașina gazdă, adică procesele care rulează în interiorul User Mode nu vor fi vizibile pentru gazdă. Acest lucru nu este asemănător cu un container Docker obișnuit, în cazul căruia mașina gazdă vede întotdeauna procesele din interiorul depozitului. Uitați-vă la acest fragment de pstree de pe unul dintre serverele mele:
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}]Și compară asta cu pstree-ul din nucleul Linux în modul utilizator:
linux─┬─5*[linux]
└─slirpAtunci când lucrez cu containere Docker, pot vedea de pe gazdă numele proceselor care sunt pornite în sistemul guest. Cu modul utilizator Linux, acest lucru nu este posibil. Ce înseamnă asta? Înseamnă că instrumentele de monitorizare care funcționează prin subsistemul de audit Linux nu văd procesele care rulează în sistemul guest. Dar în anumite situații, această caracteristică poate deveni o sabie cu două tăișuri.
De fapt, întregul post de mai jos este un set de cercetări și încercări brute de a obține rezultatul dorit. Pentru asta a trebuit să folosesc diferite unelte vechi, să citesc codul sursă al nucleului, să fac depanare intensivă a codului scris pe vremea când încă eram în școala primară, precum și să înțeleg construcțiile Heroku cu ajutorul unui binar special în căutarea uneltelor de care aveam nevoie. Toată această muncă a făcut ca băieții din IRC-ul meu să mă numească vrăjitoare (magic). Sper că acest post va fi o documentație utilă pentru cineva care vrea să facă exact același lucru, dar cu nuclee și versiuni de OS mai noi.
Configurare
Configurarea Modului Utilizator Linux se realizează în câteva etape:
- instalarea dependențelor pe gazdă;
- descărcarea nucleului Linux;
- configurarea construcției nucleului;
- construirea nucleului;
- instalarea binarului;
- configurarea sistemului de fișiere guest;
- găsirea parametrilor de pornire a nucleului;
- configurarea rețelei guest;
- pornirea nucleului guest.
Presupun că dacă decideți să faceți totul singur, cel mai probabil veți lucra într-un sistem similar cu Ubuntu sau Debian. Am încercat să implementez tot ceea ce am menționat mai sus în distribuția mea preferată — Alpine, dar nu am reușit, aparent din cauza că nucleul Linux are o legătură rigidă cu glibc-ism pentru driverele în modul utilizator. Planuiesc să raportez acest lucru upstream după ce voi înțelege complet problema.
Instalarea dependențelor pe gazdă
Ubuntu necesită, ca minim, următoarele pachete pentru compilarea nucleului Linux (în cazul unei instalări curate):
- 'build-essential'
- 'flex'
- 'bison'
- 'xz-utils'
- 'wget'
- 'ca-certificates'
- 'bc'
- 'linux-headers'
Le puteți instala folosind următoarea comandă (cu drepturi de root sau folosind sudo):
apt-get -y install build-essential flex bison xz-utils wget ca-certificates bc
linux-headers-$(uname -r) Rețineți că rularea programului de configurare a meniului pentru nucleul Linux va necesita instalarea libncurses-dev. Vă rog să vă asigurați că este instalat folosind următoarea comandă (cu drepturi de root sau folosind sudo):
apt-get -y install libncurses-devDescărcarea nucleului
Stabiliți un loc pentru descărcarea și apoi compilarea nucleului. Pentru această operație, va trebui să alocați aproximativ 1,3 GB de spațiu pe hard disk, așa că asigurați-vă că aveți cât este necesar.
După aceea, accesați și obțineți URL-ul pentru descărcarea celei mai recente versiuni stabile a nucleului. La momentul redactării acestui articol, acesta este:
Descărcați acest fișier folosind 'wget':
wget https://cdn.kernel.org/pub/linux/kernel/v5.x/linux-5.1.16.tar.xz Și extrageți-l folosind 'tar':
tar xJf linux-5.1.16.tar.xzAcum intrăm în directorul creat în urma extragerii tarball-ului:
cd linux-5.1.16Configurarea compilării nucleului
Sistemul de compilare a nucleului este un ensemble de cu numeroase instrumente și scripturi personalizate pentru automatizarea procesului. Pentru început, deschideți programul interactiv de configurare:
make ARCH=um menuconfig Acesta va gestiona parțial compilarea și vă va afișa o fereastră de dialog. Când în partea de jos a ferestrei apare ‘[Select]‘, veți putea să începeți configurarea folosind tastelerele Space sau Enter. Navigarea prin fereastră se face, ca de obicei, cu săgețile „sus” și „jos”, iar selectarea elementelor — „stânga” sau „dreapta”.
Un indicator de tip —> indică faptul că sunteți în submeniul cărui intrare se face prin tasta Enter. Ieșirea din acesta se face, evident, cu ‘[Exit]‘.
Activați următoarele opțiuni în ‘[Select]‘ și asigurați-vă că lângă ele există simbolul ‘[*]’:
Opțiuni specifice UML:
- Sistem de fișiere gazdă
Suport de rețea (activați aceasta pentru a face meniul să apară):
- Opțiuni de rețea:
- Rețea TCP/IP
Dispozitive de rețea UML:
- Dispozitiv de rețea virtual
- Transport SLiRP Din această fereastră, puteți ieși consecutiv selectând ‘[Exit]‘. Asigurați-vă doar că la sfârșit veți fi întrebat dacă doriți să salvați configurația și alegeți ‘[Yes]‘.
Vă recomand să experimentați cu parametrii de compilare a nucleului după ce ați citit acest articol. Prin aceste experimente, veți putea învăța multe despre cum funcționează mecanismele de bază ale nucleului și despre influența diferitelor steaguri asupra compilării acestuia.
Compilarea nucleului
Nucleul Linux este un program mare care se ocupă cu o mulțime de lucruri. Chiar și cu o configurație minimă pe un hardware mai vechi, compilarea sa poate dura destul de mult timp. Așadar, compilarea nucleului se face cu următoarea comandă:
make ARCH=um -j$(nproc) De ce? Această comandă va spune compilatorului nostru să folosească toate nucleele și firele de procesare disponibile în procesul de compilare. Comanda $(nproc) la finalul Build introduce ieșirea comenzii nproc, care face parte din coreutils în configurația standard a Ubuntu.
După un timp, nucleul nostru va fi compilat într-un fișier executabil ./linux.
Instalarea binarului
Având în vedere că User Mode în Linux creează un binar obișnuit, îl puteți instala ca pe orice altă utilitate. Iată cum am făcut eu:
mkdir -p ~/bin
cp linux ~/bin/linux De asemenea, este bine să vă asigurați că ~/bin este în PATH-ul dumneavoastră. $PATH:
export PATH=$PATH:$HOME/binConfigurarea sistemului de fișiere pentru maşina virtuală
Creați un director pentru sistemul de fișiere al maşinii virtuale:
mkdir -p $HOME/prefix/uml-demo
cd $HOME/prefix
Deschideți alpinelinux.org și în găsiți linkul actual pentru descărcare MINI ROOT FILESYSTEM. La momentul scrierii acestui articol, acesta era:
http://dl-cdn.alpinelinux.org/alpine/v3.10/releases/x86_64/alpine-minirootfs-3.10.0-x86_64.tar.gzDescărcați acest tarball folosind 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.gzAcum intrați în directorul sistemului de fișiere al maşinii virtuale și extrageți arhiva:
cd uml-demo
tar xf ../alpine-rootfs.tgzAcțiunile descrise vor crea un mic șablon de sistem de fișiere. Datorită caracteristicilor de funcționare ale sistemului, instalarea pachetelor prin managerul apk Alpine va fi extrem de dificilă. Dar acest FS va fi suficient pentru a evalua ideea generală.
De asemenea, va fi nevoie de instrumentul pentru a preveni consumul de memorie ale nucleului nostru virtual.
wget -O tini https://github.com/krallin/tini/releases/download/v0.18.0/tini-static
chmod +x tiniCrearea liniei de comandă a nucleului
În nucleul Linux, ca și în majoritatea altor programe, există argumente pentru linia de comandă, disponibile prin specificarea cheii --help.
personalizat —help
linux --help
User Mode Linux v5.1.16
disponibil la http://user-mode-linux.sourceforge.net/
--showconfig
Afișează fișierul de configurare din care a fost generat acest binar UML.
iomem=,
Configurează ca o regiune de memorie IO numită .
mem=
Acesta controlează câtă memorie „fizică” alocă nucleul
pentru sistem. Dimensiunea este specificată ca un număr urmat de
una dintre literele 'k', 'K', 'm', 'M', care au semnificațiile evidente.
Aceasta nu este legată de cantitatea de memorie din gazdă. Poate
fi mai mult, iar excesul, dacă este vreodată folosit, va fi pur și simplu swap-uit.
Exemplu: mem=64M
--help
Afișează acest mesaj.
debug
acest flag nu este necesar pentru a rula gdb pe UML în modul skas
root=
Acesta este de fapt folosit de nucleul generic exact în același
mod ca și în orice alt nucleu. Dacă configurezi mai multe dispozitive
de bloc și vrei să bootezi de pe altceva decât ubd0, ar trebui să
folosești ceva de genul:
root=/dev/ubd5
--version
Afișează numărul versiunii nucleului.
umid=
Acesta este folosit pentru a atribui o identitate unică acestei mașini UML și
este folosit pentru a numi fișierul pid și socketul consolei de management.
con[0-9]*=
Atașează o consolă sau o linie serială la un canal gazdă. Vezi
http://user-mode-linux.sourceforge.net/old/input.html pentru o descriere completă
a acestui comutator.
eth[0-9]+=,
Configurează un dispozitiv de rețea.
aio=2.4
Acesta este folosit pentru a forța UML să utilizeze stilul AIO 2.4 chiar și atunci când AIO 2.6 este
disponibil. AIO 2.4 este un fir unic care gestionează o solicitare la
un moment dat, sincron. AIO 2.6 este un fir care folosește interfața AIO 2.6
pentru a gestiona un număr arbitrar de solicitări pendente. AIO 2.6
nu este disponibil în modul tt, pe gazde 2.4, sau când UML este construit cu
/usr/include/linux/aio_abi.h care nu este disponibil. Multe distribuții nu includ
aio_abi.h, așa că va trebui să-l copiezi dintr-un arbore de nucleu în
/usr/include/linux pentru a construi un UML capabil AIO
nosysemu
Dezactivează patch-ul de emulare a syscall pentru ptrace (SYSEMU).
SYSEMU este un patch de performanță introdus de Laurent Vivier. Schimbă
comportamentul ptrace() și ajută la reducerea ratelor de schimbare a contextului gazdei.
Pentru a-l face să funcționeze, ai nevoie de un patch pentru nucleul gazdei, de asemenea.
Vezi http://perso.wanadoo.fr/laurent.vivier/UML/ pentru mai multe
informații.
uml_dir=
Locația unde să plasezi fișierele pid și umid.
quiet
Dezactivează mesajele de informații în timpul pornirii.
hostfs=,,...
Acesta este folosit pentru a seta parametrii hostfs. Argumentul
directorului rădăcină este folosit pentru a restricționa toate montările hostfs
în interiorul arborelui de director specificat pe gazdă. Dacă acest lucru nu este specificat,
atunci un utilizator din UML poate monta orice în gazdă care este accesibil utilizatorului care rulează
acesta. Singurul flag suportat în prezent este 'append', care specifică că toate
fișierele deschise de hostfs vor fi deschise în modul append.Acest panou luminează principalele parametrii de pornire. Să pornim nucleul cu un set minim necesar de opțiuni:
linux
root=/dev/root
rootfstype=hostfs
rootflags=$HOME/prefix/uml-demo
rw
mem=64M
init=/bin/shLinile de mai sus spun nucleului nostru următoarele:
- Să presupunem că sistemul de fișiere root este un dispozitiv pseudonim
/dev/root. - Alege ca un driver pentru sistemul de fișiere rădăcină.
- Montează sistemul de fișiere al oaspeților pe care l-am creat în dispozitivul rădăcină.
- Și da, în modul citire-scriere.
- Folosește doar 64 de megabaiți de memorie RAM (poți folosi mult mai puțin, în funcție de ceea ce planifici să faci, dar 64 MB par a fi volumul optim).
- Kernelul pornește automat
/bin/shcuminit-procesul.
Execută această comandă și ar trebui să primești ceva de genul următor:
Încă o foaie
Limite de dump de kernel :
moale - 0
dure - NIMIC
Verificarea dacă ptrace poate schimba numerele apelurilor de sistem...OK
Verificarea patch-ului de emulare a apelului de sistem pentru ptrace...OK
Verificarea patch-ului avansat de emulare a apelului de sistem pentru ptrace...OK
Verificarea variabilelor de mediu pentru un director temporar...niciunul găsit
Verificarea dacă \/dev\shm este pe tmpfs...OK
Verificarea PROT_EXEC mmap în \/dev\shm...OK
Adăugând 32137216 bytes la memoria fizică pentru a compensa gap-ul exec-shield
Versiunea Linux 5.1.16 (cadey@kahless) (gcc versiunea 7.4.0 (Ubuntu 7.4.0-1ubuntu1~18.04.1)) #30 Dum 7 Iul 7 18:57:19 UTC 2019
Construit 1 zonelists, gruparea mobilității activată. Total pagini: 23898
Linia de comandă a kernelului: root=\/dev\/root rootflags=\/home\/cadey\/dl\/uml\/alpine rootfstype=hostfs rw mem=64M init=\/bin\/sh
Intrările tabelului de hash al cache-ului Dentry: 16384 (ordonare: 5, 131072 bytes)
Intrările tabelului de hash al cache-ului Inode: 8192 (ordonare: 4, 65536 bytes)
Memorie: 59584K\/96920K disponibile (2692K cod kernel, 708K rwdata, 588K rodata, 104K init, 244K bss, 37336K rezervate, 0K cma-rezervate)
SLUB: HWalign=64, Ordine=0-3, MinObjects=0, CPUs=1, Noduri=1
NR_IRQS: 15
sursa cronometru: timer: mască: 0xffffffffffffffff max_cycles: 0x1cd42e205, max_idle_ns: 881590404426 ns
Calibrând ciclul de întârziere... 7479.29 BogoMIPS (lpj=37396480)
pid_max: default: 32768 minim: 301
Intrările tabelului de hash al cache-ului Mount: 512 (ordonare: 0, 4096 bytes)
Intrările tabelului de hash al cache-ului Mountpoint: 512 (ordonare: 0, 4096 bytes)
Verificarea suportului ptys al gazdei pentru ieșiri SIGIO...Da
Verificarea suportului SIGIO al gazdei pe închidere...Nu, activând soluția alternativă
devtmpfs: inițializat
random: get_random_bytes apelat din setup_net+0x48\/0x1e0 cu crng_init=0
Folosind 2.6 gazdă AIO
sursa cronometru: jiffies: mască: 0xffffffff max_cycles: 0xffffffff, max_idle_ns: 19112604462750000 ns
Intrările tabelului de hash futex: 256 (ordonare: 0, 6144 bytes)
NET: Protocolul familiei 16 a fost înregistrat
sursa cronometru: Schimbat pe sursa cronometru timer
NET: Protocolul familiei 2 a fost înregistrat
tcp_listen_portaddr_hash intrările tabelului de hash: 256 (ordonare: 0, 4096 bytes)
TCP hash intrările tabelului de hash stabilit: 1024 (ordonare: 1, 8192 bytes)
TCP hash intrările tabelului de hash bind: 1024 (ordonare: 1, 8192 bytes)
TCP: Tabelele de hash au fost configurate (stabilite 1024 bind 1024)
UDP hash intrările tabelului de hash: 256 (ordonare: 1, 8192 bytes)
UDP-Lite hash intrările tabelului de hash: 256 (ordonare: 1, 8192 bytes)
NET: Protocolul familiei 1 a fost înregistrat
console [stderr0] dezactivată
mconsole (versiunea 2) inițializată pe \/home\/cadey\/.uml\/tEwIjm\/mconsole
Verificarea suportului MADV_REMOVE al gazdei...OK
workingset: timestamp_bits=62 max_order=14 bucket_order=0
Driverul SCSI generic (bsg) de strat bloc versiunea 0.4 a fost încărcat (major 254)
schedulerul de io noop înregistrat (default)
schedulerul de io bfq înregistrat
loop: modul încărcat
NET: Protocolul familiei 17 a fost înregistrat
Driverul consolei stdio inițializat
Folosind un tip de canal configurat în afara UML
setup_one_line a eșuat pentru dispozitivul 1 : Configurarea a eșuat
Folosind un tip de canal configurat în afara UML
setup_one_line a eșuat pentru dispozitivul 2 : Configurarea a eșuat
Folosind un tip de canal configurat în afara UML
setup_one_line a eșuat pentru dispozitivul 3 : Configurarea a eșuat
Folosind un tip de canal configurat în afara UML
setup_one_line a eșuat pentru dispozitivul 4 : Configurarea a eșuat
Folosind un tip de canal configurat în afara UML
setup_one_line a eșuat pentru dispozitivul 5 : Configurarea a eșuat
Folosind un tip de canal configurat în afara UML
setup_one_line a eșuat pentru dispozitivul 6 : Configurarea a eșuat
Folosind un tip de canal configurat în afara UML
setup_one_line a eșuat pentru dispozitivul 7 : Configurarea a eșuat
Folosind un tip de canal configurat în afara UML
setup_one_line a eșuat pentru dispozitivul 8 : Configurarea a eșuat
Folosind un tip de canal configurat în afara UML
setup_one_line a eșuat pentru dispozitivul 9 : Configurarea a eșuat
Folosind un tip de canal configurat în afara UML
setup_one_line a eșuat pentru dispozitivul 10 : Configurarea a eșuat
Folosind un tip de canal configurat în afara UML
setup_one_line a eșuat pentru dispozitivul 11 : Configurarea a eșuat
Folosind un tip de canal configurat în afara UML
setup_one_line a eșuat pentru dispozitivul 12 : Configurarea a eșuat
Folosind un tip de canal configurat în afara UML
setup_one_line a eșuat pentru dispozitivul 13 : Configurarea a eșuat
Folosind un tip de canal configurat în afara UML
setup_one_line a eșuat pentru dispozitivul 14 : Configurarea a eșuat
Folosind un tip de canal configurat în afara UML
setup_one_line a eșuat pentru dispozitivul 15 : Configurarea a eșuat
Console inițializată pe \/dev\/tty0
console [tty0] activată
console [mc-1] activată
A eșuat inițializarea dispozitivului ubd 0 : N-a putut determina dimensiunea fișierului dispozitivului
VFS: Montat root (sistem de fișiere hostfs) pe dispozitivul 0:11.
devtmpfs: montat
Această arhitectură nu are protecția memoriei kernel.
Rulați \/bin\/sh ca proces init
\/bin\/sh: nu se poate accesa tty; controlul locului de muncă oprit
random: inițiere rapidă finalizată
\/ # Manipulările de mai sus ne vor oferi un sistem de găzduire minim, fără lucruri precum /proc sau un nume de gazdă alocat. De exemplu, încercați să executați comenzile următoare:
- uname -av
- cat /proc/self/pid
- hostname
Pentru a ieși din sistemul găzduit, introduceți exit sau apăsați control-d. Aceasta va termina shell-ul cu un panic kernel ulterior:
/ # exit
Kernel panic - not syncing: Attempted to kill init! exitcode=0x00000000
fish: “./linux root=/dev/root rootflag…” terminated by signal SIGABRT (Abort) Am obținut această panică kernel din cauza faptului că nucleul Linux consideră că procesul de inițializare este întotdeauna activ. Fără el, sistemul nu mai poate funcționa și se oprește. Dar, fiind un proces de modul utilizator, rezultatul obținut se trimite singur la SIGABRT, ceea ce duce la ieșire.
Configurarea rețelei găzdite
Aici lucrurile încep să nu meargă conform planului. Rețeaua în User Mode Linux este locul unde întreaga conceptie a „modului utilizator” începe să se destrame. De obicei, la nivel de sistem, rețeaua este limitată modurilor de execuție privilegiate din motive bine cunoscute de toată lumea.
Note: mai multe despre diferitele opțiuni de lucru cu rețeaua în UML pot fi citite .
Călătorie în slirp
Totuși, există un instrument vechi și practic nesuportat numit , prin care User Mode Linux poate interacționa cu rețeaua. Funcționează aproximativ ca un stack TCP/IP la nivel de utilizator și nu necesită permisiuni de sistem pentru a fi pornit. Acest instrument a fost lansat în 1995, iar ultima actualizare datează din 2006.Slirp este foarte vechi. De-a lungul timpului fără suport și actualizări, compilatoarele au evoluat atât de mult încât acest instrument poate fi caracterizat doar ca .
Așadar, să instalăm Slirp din repozitoarele Ubuntu și să încercăm să-l pornim:
sudo apt-get install slirp
/usr/bin/slirp
Slirp v1.0.17 (BETA)
Copyright (c) 1995,1996 Danny Gasparovski și alții.
Toate drepturile rezervate.
Acest program este protejat prin drepturi de autor, software gratuit.
Vă rugăm să citiți fișierul COPYRIGHT care a venit cu pachetul Slirp
pentru termenii și condițiile dreptului de autor.
Adresa IP a gazdei Slirp: 127.0.0.1
Adresa IP a DNS-urilor dvs.: 1.1.1.1, 10.77.0.7
Adresa dumneavoastră este 10.0.2.15
(sau orice altceva doriți)
Tastați cinci zerouri (0) pentru a ieși.
[autodetectare SLIP/CSLIP, MTU 1500, MRU 1500, 115200 baud]
SLiRP Gata ...
fish: “/usr/bin/slirp” terminat prin semnal SIGSEGV (eroare de limită de adresă)Oh, Doamne. Să instalăm un debugger pentru Slirp și să vedem dacă putem înțelege ce se întâmplă aici:
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.Eroarea apare în . Să ne uităm la stacktrace, poate ne va ajuta cu ceva:
(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.Aici vedem că eroarea se produce în timpul pornirii buclei principale, când slirp încearcă să verifice timeout-urile. Aici ar fi trebuit să renunț la încercările de depanare. Dar să vedem dacă funcționează Slirp compilat din surse. Am descărcat din nou arhiva direct de pe site , deoarece să tragi ceva de acolo prin terminal — e o durere:
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 Aici vedem alerte despre funcții încorporate neclare, adică imposibilitatea de a lega fișierul binar rezultat. Se pare că între anul 2006 și acest moment gcc a încetat să mai creeze simboluri utilizate în funcțiile încorporate ale fișierelor compilate intermediar. Să încercăm să înlocuim cuvântul cheie inline cu un comentariu gol și să vedem rezultatul:
vi slirp.h
:6
a
#define inline /**/
:wq
makeNu. Acesta nu funcționează nici. Continuă să nu găsească simbolurile acestor funcții.
În această etapă, m-am dat bătută și am început să caut pe Github . Teoria mea era că în vreun pachet de construire Heroku ar trebui să existe fișierele binare de care aveam nevoie. În final, căutările m-au condus . Am descărcat și dezarhivat uml.tar.gz și am găsit următoarele:
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*Acesta este fișierul binar slirp! Și funcționează?
.\/slirp
Slirp v1.0.17 (BETA) FULL_BOLT
Copyright (c) 1995,1996 Danny Gasparovski și alții.
Toate drepturile rezervate.
Acest program este protejat prin copyright, software gratuit.
Vă rugăm să citiți fișierul COPYRIGHT care a venit cu pachetul Slirp
pentru termenii și condițiile dreptului de autor.
Adresa IP a gazdei Slirp: 127.0.0.1
Adresele IP ale DNS-ului dumneavoastră: 1.1.1.1, 10.77.0.7
Adresa dumneavoastră este 10.0.2.15
(sau orice altceva doriți)
Tastați cinci zerouri (0) pentru a ieși.
[autodetect SLIP/CSLIP, MTU 1500, MRU 1500]
SLiRP Pregătit ...
Nu se oprește — deci ar trebui să funcționeze! Să introducem acest binar în ~\/bin\/slirp:
cp slirp ~\/bin\/slirpPentru cazul în care creatorul pachetului îl șterge, am .
Configurarea rețelei
Acum să configurăm rețeaua pe nucleul nostru gazdă. :
linux
root=\/dev\/root
rootfstype=hostfs
rootflags=$HOME\/prefix\/uml-demo
rw
mem=64M
eth0=slirp,,$HOME\/bin\/slirp
init=\/bin\/shAcum haideți să activăm rețeaua:
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 Primele două comenzi de configurare /proc și /sys sunt necesare pentru a funcționa ifconfig, care stabilește interfața de rețea pentru comunicarea cu Slirp. Comanda route stabilește tabelul de rutare al nucleului pentru a forța trimiterea totului trafic prin tunelul Slirp. Să verificăm asta cu o cerere DNS:
nslookup google.com 8.8.8.8
Server: 8.8.8.8
Adresă 1: 8.8.8.8 dns.google
Nume: google.com
Adresă 1: 172.217.12.206 lga25s63-in-f14.1e100.net
Adresă 2: 2607:f8b0:4006:81b::200e lga25s63-in-x0e.1e100.netFuncționează!
Notă: Se pare că postarea inițială a fost scrisă pe un desktop cu un adaptor de rețea cablat, sau cu o altă configurație care nu necesită drivere suplimentare. Pe un laptop cu WiFi 8265 de la Intel, când se activează rețeaua, apare o eroare
/ # 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
/ #Se pare că nucleul nu poate comunica cu driverul adaptorului de rețea. Încercarea de a compila firmware-ul în nucleu nu a rezolvat situația, din păcate. La momentul publicării, în această configurație nu s-a găsit o soluție. În configurații mai simple (de exemplu, în Virtualbox) interfața se activează corect.
Hai să automatizăm redirecționarea folosind următorul 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/shȘi să-l facem executabil:
chmod +x init.shApoi, să facem modificările în linia de comandă a nucleului:
linux
root=\/dev\/root
rootfstype=hostfs
rootflags=$HOME\/prefix\/uml-demo
rw
mem=64M
eth0=slirp,,$HOME\/bin\/slirp
init=\/init.sh
Și repetăm:
SLiRP Dedicat...
rețea configurată
\/bin\/sh: nu se poate accesa tty; controlul jobului oprit
nslookup google.com 8.8.8.8
Server: 8.8.8.8
Adresă 1: 8.8.8.8 dns.google
Nume: google.com
Adresă 1: 172.217.12.206 lga25s63-in-f14.1e100.net
Adresă 2: 2607:f8b0:4004:800::200e iad30s09-in-x0e.1e100.netRețeaua funcționează stabil!
Fișier Docker
Ca să îți fie mai ușor să verifici totul, am pregătit , care automatizează majoritatea pașilor descriși și ar trebui să-ți ofere o configurație funcțională. De asemenea, am , care conține tot ce este descris în postare. Dar este important să înțelegi că aici am prezentat doar configurația minimală.
Sper că acest post te-a ajutat să înțelegi cum să ridici un nucleu virtual. A ieșit ceva monstruos, dar am gândit publicarea ca un ghid cuprinzător pe tema compilării, instalării și configurării User Mode în Linux pentru versiunile moderne ale acestui sistem de operare. Pașii următori ar trebui să includă instalarea serviciilor și a altor software deja în interiorul sistemului virtual. Deoarece imaginile containerelor Docker sunt doar arhive tar desfăcute, ar trebui să poți extrage imaginea prin docker export, apoi să definești calea pentru instalarea sa în rădăcina sistemului de fișiere al nucleului virtual. Apoi, execută scriptul shell.
Mulțumiri speciale lui Rkeene de la #lobsters pe Freenode. Fără ajutorul său în depanarea Slirp, nu aș fi ajuns atât de departe. Nu am idee cum funcționează corect sistemul său Slackware cu slirp, dar sistemele mele Ubuntu și Alpine n-au acceptat slirp și binarul pe care mi l-a oferit Rkeene. Dar sunt mulțumită că am reușit să fac ceva să funcționeze.
Sursa: habr.com
