Как да започнем да използваме User Mode в Linux

Въведение от преводача: В контекста на масовото навлизане на различни контейнери в живота ни, би било интересно и полезно да разберем от какви технологии е започнало всичко това. Някои от тях могат да се прилагат и днес с полза, но не всеки си спомня (или знае, ако не е изпуснал бурното им развитие). Една от тези технологии е User Mode Linux. Авторът на оригинала направи задълбочено проучване, за да разбере какво от старите разработки все още работи, а какво не, и събра нещо като стъпка-по-стъпка указания за това как да си създадете домашен UML през 2019 година. И да, поканихме автора на оригиналния пост в Хабр Cadey, така че ако имате въпроси — задавайте на английски в коментарите.

Как да започнем да използваме User Mode в Linux

User Mode в Linux е фактически порт на ядрото на Linux сам на себе си. Този режим позволява да се стартира полнофункционално ядро на Linux като потребителски процес и обикновено се използва от разработчици за тестване на драйвери. Но този режим е полезен и като инструмент за обща изолация, принципът на който е подобен на работата на виртуални машини. Този режим осигурява по-висока изолация от Docker, но по-ниска в сравнение с пълноценна виртуална машина като KVM или Virtual Box.

Като цяло, User Mode може да изглежда странен и сложен за използване инструмент, но той все пак има свои области на приложение. В крайна сметка това е напълно функциониращо ядро на Linux, работещо от непривилегирован потребител. Тази особеност позволява да се стартира потенциално ненадежден код без никакви заплахи за хост машината. А тъй като това е пълноценен ядро, неговите процеси са изолирани от хост машината, тоест процесите, работещи в User Mode, няма да бъдат видими за хоста. Това не е подобно на познатите Docker контейнери, в които хост машината винаги вижда процесите вътре в хранилището. Погледнете този фрагмент pstree от един от моите сървъри:

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

И сравнете това с pstree на ядрото на Linux в User Mode:

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

Когато работя с Docker контейнери, мога да видя от хоста имената на процесите, които са стартирани в гостуващата система. С Linux User Mode това е невъзможно. Какво означава това? Това означава, че инструментите за мониторинг, работещи чрез подсистемата за одит на Linux (Linux’s auditing subsystem) не виждат процесите, които се изпълняват в гостуващата система. Но в някои случаи тази особеност може да се окаже двуостър нож.

По принцип целият пост по-долу е набор от изследвания и груби опити да постигна желаните резултати. За това ми се наложи да използвам различни стари инструменти, да чета изходния код на ядрото, да правя интензивно отстраняване на грешки в код, написан в едни времена, когато още ходех в начално училище, и да се ровя в сборките на Heroku с помощта на специален бинарен файл в търсене на нужните ми инструменти. Цялата тази работа доведе до това, че момчетата в моя IRC започнаха да ме наричат магьосница. Надявам се този пост да послужи на някого като надеждна документация, за да свършат същото, но с по-нови ядра и версии на ОС.

Настройка

Настройката на Linux User Mode се извършва в няколко стъпки:

  • инсталиране на зависимости на хоста;
  • изтегляне на ядрото на Linux;
  • настройка на конфигурацията на ядрото;
  • сборка на ядрото;
  • инсталиране на бинарния файл;
  • настройка на гостуващата файлова система;
  • избор на параметри за стартиране на ядрото;
  • настройка на гостуващата мрежа;
  • стартиране на гостуващото ядро.

Предполагам, че ако решите сами да извършите всичко това, вероятно ще работите в някаква система, подобна на Ubuntu или Debian. Опитах се да реализирам всичко по-горе в любимия ми дистрибутив — Alpine, но не успях, явно защото ядрото на Linux е стриктно обвързано с glibc-измите за драйверите в User Mode. Планирам да се свържа с upstream след като окончателно се запозная с проблема.

Инсталиране на зависимостите на хоста

Ubuntu изисква поне следните пакети за компилиране на ядрото на Linux (при условие че е направена чиста инсталация):

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

Можете да ги инсталирате с помощта на следната команда (с права root или с помощта на sudo):

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

Обърнете внимание, че стартирането на програмата за настройка на менюто за ядрото на Linux ще изисква инсталиране на libncurses-dev. Моля, уверете се, че е инсталиран с помощта на следната команда (с права root или с помощта на sudo):

apt-get -y install libncurses-dev

Сваляне на ядрото

Определете местоположението за изтегляне и последващо изграждане на ядрото. За тази операция ще ви трябват около 1,3 Гб пространство на твърдия диск, така че се уверете, че разполагате с него.

След това посетете kernel.org и получете URL адрес за изтегляне на последната стабилна версия на ядрото. Към момента на писане на публикацията това е: https://cdn.kernel.org/pub/linux/kernel/v5.x/linux-5.1.16.tar.xz

Свалете този файл, използвайки 'wget':

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

И разархивирайте го с помощта на 'tar':

tar xJf linux-5.1.16.tar.xz

Сега влезте в директорията, създадена при разархивирането на tarball:

cd linux-5.1.16

Настройка на изграждането на ядрото

Системата за изграждане на ядрото е набор от Make-файлове с и множество потребителски инструменти и скриптове за автоматизация на процеса. За начало отворете интерактивната програма за настройка:

make ARCH=um menuconfig

Тя частично ще проведе изграждането и ще ви покаже диалогов прозорец. Когато в долната част на прозореца се появи ‘[Select]‘, ще можете да започнете настройката с клавишите Space или Enter. Навигацията в прозореца става, както обикновено, с клавишите със стрелки ‘нагоре’ и ‘надолу’, а изборът на елементи — ‘наляво’ или ‘надясно’.

Индикаторът ‘->’ означава, че сте в подменю, за влизане в което се използва клавишът Enter. Изходът от него, очевидно, става чрез ‘[Exit]‘.

Активирайте следните опции в ‘[Select]‘ и се уверете, че до тях има символ ‘[*]’:

Възможности, специфични за UML:
  - Хост файлова система
Поддръжка на мрежа (активирайте това, за да се появи подменюто):
  - Мрежови опции:
    - TCP/IP мрежова свързаност
Устройства UML мрежа:
  - Виртуално мрежово устройство
  - SLiRP транспорт

Всичко, от това прозорец можете да излезете, последователно избирайки ‘[Exit]‘. Само се уверете, че в края ще ви предложи да запазите конфигурацията и изберете ‘[Yes]‘.

Препоръчвам ви да експериментирате с параметрите за сглобяване на ядрото след прочитането на този пост. Чрез тези експерименти можете да научите много относно разбирането на работата на нискоуровневите механизми на ядрото и влиянието на различните флагове върху неговото изграждане.

Сглобяване на ядро

Ядрото на Linux е голяма програма, която се занимава с много неща. Дори при такава минимална конфигурация на старо оборудване, сглобяването му може да отнеме известно време. Ето защо сглобявайте ядрото с помощта на следната команда:

make ARCH=um -j$(nproc)

Защо? Тази команда ще каже на нашия компилатор да използва всички налични ядра и потоци на процесора в процеса на сглобяване. Командата $(nproc) накрая подставя извода на командата nproc, която е част от coreutils в стандартната инсталация на Ubuntu.

След известно време нашето ядро ще бъде сглобено в изпълняем файл ./linux.

Инсталиране на бинарния файл

Тъй като User Mode в Linux създава обикновен бинарен файл, можете да го инсталирате, както всяка друга утилита. Ето как го направих аз:

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

Също така се уверете, че ~/bin се намира в вашия $PATH:

export PATH=$PATH:$HOME/bin

Настройка на гостовата файлова система

Създайте директория за гостовата файлова система:

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

Отворете alpinelinux.org и в раздела за изтегляния намерете актуалната връзка за изтегляне MINI ROOT FILESYSTEM. По време на писането на публикацията, това беше:

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

Свалете този tarball, използвайки 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

Сега влезте в директорията на гостовата файлова система и разархивирайте архива:

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

Описаните действия ще създадат малък шаблон на файлова система. Поради особеностите на работата на системата, инсталирането на пакети чрез диспетчера apk на Alpine ще бъде изключително трудно. Но тази файлова система ще е достатъчна, за да оцените общата идея.

Също така ще ни трябва инструментът tini за предотвратяване на консумацията на памет от зомби процесите на нашето гостово ядро.

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

Създаване на командния ред на ядрото

В ядрото на Linux, както и в повечето други програми, има аргументи на командния ред, за които можете да се запознаете, посочвайки ключа --help.

Сам —help

linux --help
User Mode Linux v5.1.16
        наличен на http://user-mode-linux.sourceforge.net/

--showconfig
    Отпечатва конфигурационния файл, от който е генериран този UML бинарен файл.

iomem=,
    Конфигурирайте  като IO паметова област с име .

mem=
    Това контролира колко "физическа" памет ядрото разпределя
    за системата. Размерът се задава като число, последвано от
    едно от 'k', 'K', 'm', 'M', които имат съответните значения.
    Това не е свързано с количеството памет на хост машината. Може
    да бъде повече, а излишъкът, ако бъде използван, просто ще бъде заменен.
        Пример: mem=64M

--help
    Отпечатва това съобщение.

debug
    този флаг не е необходим, за да стартирате gdb на UML в skas режим

root=
    Това всъщност се използва от генеричното ядро по точно същия
    начин, както в което и да е друго ядро. Ако конфигурирате редица блокови
    устройства и искате да стартирате от нещо различно от ubd0, ще
    трябва да използвате нещо подобно:
        root=/dev/ubd5

--version
    Отпечатва номера на версията на ядрото.

umid=
    Това се използва за задаване на уникален идентификатор на тази UML машина и
    се използва за именуване на файла на pid и сокета на управленската конзола.

con[0-9]*=
    Свържете конзола или серийна линия към хост канал.  Вижте
    http://user-mode-linux.sourceforge.net/old/input.html за пълно
    описание на тази опция.

eth[0-9]+=,
    Конфигурирайте мрежово устройство.
    
aio=2.4
    Това се използва, за да накара UML да използва AIO в стил 2.4, дори когато 2.6 AIO е
    наличен.  2.4 AIO е един поток, който обработва едно запитване в
    даден момент, синхронно.  2.6 AIO е поток, който използва 2.6 AIO
    интерфейса, за да обработва произволен брой очакващи запитвания.  2.6 AIO
    не е наличен в tt режим, на 2.4 хостове или когато UML е компилирано с
    /usr/include/linux/aio_abi.h, която не е налична.  Много разпространения не
    включват aio_abi.h, така че ще трябва да го копирате от ядрото в
    вашето /usr/include/linux, за да създадете AIO-съвместим UML

nosysemu
    Изключва патча за емулация на системни извиквания за ptrace (SYSEMU).
    SYSEMU е патч за производителност, въведен от Лоран Вивиер. Той променя
    поведението на ptrace() и помага да се намалят честотата на контекстни
    превключвания на хоста. За да работи, се нуждаете от патч на ядрото
    за вашия хост, също.
    Вижте http://perso.wanadoo.fr/laurent.vivier/UML/ за допълнителна
    информация.

uml_dir=
    Местоположението за поставяне на файловете pid и umid.

quiet
    Изключва информационните съобщения по време на зареждане.

hostfs=,,...
    Това се използва за задаване на параметри на hostfs.  Аргументът за
    кореновата директория се използва, за да ограничи всички хост fs
    монтирания в рамките на зададеното дърво от директории на хоста.
    Ако това не е зададено, тогава потребител в UML може да монтира
    всичко на хоста, което е достъпно за потребителя, който го изпълнява.
    Единственият поддържан флаг в момента е 'append', който посочва, че
    всички файлове, отворени от hostfs, ще бъдат отворени в режим на добавяне.

Тази публикация осветлява основните параметри за стартиране. Нека стартираме ядрото с минимален набор от необходими опции:

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

Строките по-горе казват на нашето ядро следното:

  • Да предположим, че кореновата файловата система е псевдо-устройство /dev/root.
  • Избери hostfs като драйвер на кореновата файлова система.
  • Монтирайте гостуващата файлова система, която създадохме в root-устройството.
  • И да, в режим на четене и запис.
  • Използвайте само 64 мегабайта оперативна памет (можете да използвате много по-малко, в зависимост от това, какво планувате да правите, но 64 MB изглеждат като оптимален обем).
  • Ядро автоматично стартира /bin/sh как init-процес.

Стартирайте тази команда и трябва да получите нещо подобно на следното:

Още един лист

Ограничения на core dump :
        меко - 0
        твърдо - НИКАКВО
Проверка дали ptrace може да промени номера на системните повиквания...ОК
Проверка за пач за емулация на системни повиквания за ptrace...ОК
Проверка за напреднал пач за емулация на системни повиквания за ptrace...ОК
Проверка на променливите на средата за временно хранилище...не е намерено
Проверка дали \/dev\/shm е на tmpfs...ОК
Проверка на PROT_EXEC mmap в \/dev\/shm...ОК
Добавяне на 32137216 байта към физическата памет, за да се компенсира празнината на exec-shield
Версия на Linux 5.1.16 (cadey@kahless) (gcc версия 7.4.0 (Ubuntu 7.4.0-1ubuntu1~18.04.1)) #30 Нед. 7 юли 2019 18:57:19 UTC
Създадени 1 списъка със зони, мобилна групировка активирана. Общо страници: 23898
Команден ред на ядрото: root=\/dev\/root rootflags=\/home\/cadey\/dl\/uml\/alpine rootfstype=hostfs rw mem=64M init=\/bin\/sh
Хеш таблица на кеша за dentry: 16384 (ред: 5, 131072 байта)
Хеш таблица на кеша за inode: 8192 (ред: 4, 65536 байта)
Памет: 59584K\/96920K налична (2692K код на ядрото, 708K rwdata, 588K rodata, 104K init, 244K bss, 37336K резервирани, 0K cma-резервирани)
SLUB: HWalign=64, Ред=0-3, MinObjects=0, CPUs=1, Възли=1
NR_IRQS: 15
часывsource: таймер: маска: 0xffffffffffffffff max_cycles: 0x1cd42e205, max_idle_ns: 881590404426 ns
Калибриране на цикъл за забавяне... 7479.29 BogoMIPS (lpj=37396480)
pid_max: по подразбиране: 32768 минимум: 301
Хеш таблица на кеша за монтиране: 512 (ред: 0, 4096 байта)
Хеш таблица на кеша за точка на монтиране: 512 (ред: 0, 4096 байта)
Проверка дали хост ptys поддържат изход SIGIO...Да
Проверка дали хост ptys поддържат SIGIO при затваряне...Не, активиране на обход
devtmpfs: инициализирано
random: get_random_bytes е извикано от setup_net+0x48\/0x1e0 с crng_init=0
Използване на 2.6 хост AIO
часывsource: jiffies: маска: 0xffffffff max_cycles: 0xffffffff, max_idle_ns: 19112604462750000 ns
futex хеш таблица записи: 256 (ред: 0, 6144 байта)
NET: Регистрирано семейство протоколи 16
часывsource: Превключено на часовник таймер
NET: Регистрирано семейство протоколи 2
tcp_listen_portaddr_hash хеш таблица записи: 256 (ред: 0, 4096 байта)
TCP установени хеш таблица записи: 1024 (ред: 1, 8192 байта)
TCP хеш таблица записи: 1024 (ред: 1, 8192 байта)
TCP: Хеш таблици конфигурирани (установени 1024 свързани 1024)
UDP хеш таблица записи: 256 (ред: 1, 8192 байта)
UDP-Lite хеш таблица записи: 256 (ред: 1, 8192 байта)
NET: Регистрирано семейство протоколи 1
конзола [stderr0] деактивирана
mconsole (версия 2) инициализирана на \/home\/cadey\/.uml\/tEwIjm\/mconsole
Проверка на поддръжката на host MADV_REMOVE...ОК
workingset: timestamp_bits=62 max_order=14 bucket_order=0
Блоков слой SCSI генерален (bsg) драйвер версия 0.4 зареден (основен 254)
io планировчик noop регистриран (по подразбиране)
io планировчик bfq регистриран
loop: модулът зареден
NET: Регистрирано семейство протоколи 17
Инициализиран stdio конзолен драйвер
Използване на тип канал, който е конфигуриран извън UML
setup_one_line не успя за устройство 1 : Конфигурацията не успя
Използване на тип канал, който е конфигуриран извън UML
setup_one_line не успя за устройство 2 : Конфигурацията не успя
Използване на тип канал, който е конфигуриран извън UML
setup_one_line не успя за устройство 3 : Конфигурацията не успя
Използване на тип канал, който е конфигуриран извън UML
setup_one_line не успя за устройство 4 : Конфигурацията не успя
Използване на тип канал, който е конфигуриран извън UML
setup_one_line не успя за устройство 5 : Конфигурацията не успя
Използване на тип канал, който е конфигуриран извън UML
setup_one_line не успя за устройство 6 : Конфигурацията не успя
Използване на тип канал, който е конфигуриран извън UML
setup_one_line не успя за устройство 7 : Конфигурацията не успя
Използване на тип канал, който е конфигуриран извън UML
setup_one_line не успя за устройство 8 : Конфигурацията не успя
Използване на тип канал, който е конфигуриран извън UML
setup_one_line не успя за устройство 9 : Конфигурацията не успя
Използване на тип канал, който е конфигуриран извън UML
setup_one_line не успя за устройство 10 : Конфигурацията не успя
Използване на тип канал, който е конфигуриран извън UML
setup_one_line не успя за устройство 11 : Конфигурацията не успя
Използване на тип канал, който е конфигуриран извън UML
setup_one_line не успя за устройство 12 : Конфигурацията не успя
Използване на тип канал, който е конфигуриран извън UML
setup_one_line не успя за устройство 13 : Конфигурацията не успя
Използване на тип канал, който е конфигуриран извън UML
setup_one_line не успя за устройство 14 : Конфигурацията не успя
Използване на тип канал, който е конфигуриран извън UML
setup_one_line не успя за устройство 15 : Конфигурацията не успя
Конзолата инициализирана на \/dev\/tty0
конзола [tty0] активирана
конзола [mc-1] активирана
Неуспешно инициализиране на ubd устройство 0 :Не можа да се определи размера на файла на устройството
VFS: Монтирано корено (hostfs файлова система) на устройство 0:11.
devtmpfs: монтирано
Тази архитектура няма защита на паметта на ядрото.
Стартирайте \/bin\/sh като процес init
\/bin\/sh: не може да се достъпи tty; контролът на работата е изключен
random: бързо init завършено
\ # 

Манипулациите по-горе ще ни дадат гостева система на минимално ниво, без такива неща като /proc или зададено хост на име. Например, опитайте да изпълните следните команди:

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

За да излезете от гостевата система, въведете exit или натиснете control-d. Това ще прекрати обвивката с последващ kernel panic:

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

Получихме този kernel panic поради факта, че ядрото на Linux счита, че процесът на инициализация винаги е стартиран. Без него системата вече не може да функционира и спира. Но тъй като това е процес от потребителския режим, полученият резултат изпраща самия себе си в SIGABRT, което води до изход.

Настройка на гостова мрежа

Тук вече нещата започват да не вървят по план. Мрежата в User Mode Linux е мястото, където цялата концепция на ограничен "потребителски режим" започва да се разпада. Обикновено на системно ниво мрежата е ограничена до привилегировани режими на изпълнение по всички ни известни причини.

Примечание: повече за различните варианти на работа с мрежата в UML можете да прочетете тук.

Пътешествие в slirp

Въпреки това съществува древен и практически неподдържан инструмент, наречен Slirp, с помощта на който User Mode Linux може да взаимодейства с мрежата. Той работи по подобие на стека TCP/IP на потребителско ниво и не изисква никакви системни разрешения за стартиране. Този инструмент бе пуснат през 1995 година, а последното обновление е датирано от 2006 година. Slirp е много стар. През времето без поддръжка и обновления, компилаторите се развиха толкова много, че сега този инструмент може да се опише само като "code rot".

И така, нека инсталираме Slirp от хранилищата на Ubuntu и да опитаме да го стартираме:

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

Copyright (c) 1995,1996 Danny Gasparovski и други.
Всички права запазени.
Тази програма е авторски материал, безплатен софтуер.
Моля, прочетете файла COPYRIGHT, който дойде с пакета Slirp
за условията и правилата на авторските права.

IP адрес на хоста Slirp: 127.0.0.1
IP адрес на вашите DNS: 1.1.1.1, 10.77.0.7
Вашият адрес е 10.0.2.15
(или каквото искате)

Въведете петнула (0), за да излезете.

[автоматично откриване SLIP/CSLIP, MTU 1500, MRU 1500, 115200 baud]

SLiRP Готово ...
fish: “/usr/bin/slirp” прекратен от сигнал SIGSEGV (грешка в адресната граница)

Ох, божечки. Нека инсталираме отладчик за Slirp и да видим дали можем да разберем какво се случва тук:

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.
Лицензия GPLv3+: GNU GPL версия 3 или по-късно 
Това е софтуер с отворен код: вие имате правото да го променяте и разпространявате.
Няма ГАРАНЦИЯ, в рамките на позволеното от закона. Напишете "show copying"
и "show warranty" за повече информация.
Този GDB е конфигуриран като "x86_64-linux-gnu".
Напишете "show configuration" за детайли за конфигурацията.
За инструкции за докладване на грешки вижте:
.
Намерете ръководството на GDB и други ресурси за документация онлайн на:
.
За помощ напишете "help".
Напишете "apropos word" за да търсите команди, свързани с "word"...
Четене на символи от /usr/bin/slirp...Четене на символи от /usr/lib/debug/.build-id/c6/2e75b69581a1ad85f72ac32c0d7af913d4861f.debug...готово.
готово.
(gdb) run
Стартиране на програмата: /usr/bin/slirp
Slirp v1.0.17 (BETA)

Copyright (c) 1995,1996 Дани Газпаровски и други.
Всички права запазени.
Тази програма е защитена с авторски права, софтуер с отворен код.
Моля, прочетете файла COPYRIGHT, който дойде с пакета Slirp
за условията и клаузите на авторското право.

IP адрес на хоста Slirp: 127.0.0.1
IP адрес на вашите DNS: 1.1.1.1, 10.77.0.7
Вашият адрес е 10.0.2.15
(или каквото искате)

Напишете пет нули (0), за да излезете.

[автооткриване SLIP/CSLIP, MTU 1500, MRU 1500, 115200 baud]

SLiRP Готово ...

Програмата получи сигнал SIGSEGV, Сегментационна грешка.
                                                    ip_slowtimo () в ip_input.c:457
457     ip_input.c: Няма такъв файл или директория.

Грешка се появява в този ред. Нека да погледнем стека, може би там ще намерим нещо полезно:

(gdb) bt full
#0  ip_slowtimo () в ip_input.c:457
        fp = 0x55784a40
#1  0x000055555556a57c в main_loop () в ./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 в main (argc=1, argv=0x7fffffffdc58) в ./main.c:95
Няма локални.

Тук виждаме, че ако сбой се случва по време на стартиране на основния цикъл, когато slirp опитва да провери тайм-аутите. В този момент трябваше да се откажа от опитите за отстраняване на грешки. Но нека видим дали Slirp, компилиран от източниците, работи. Преинсталирах архива директно от сайта Sourceforge, защото свалянето на нещо от там през командния ред е болно:

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

Тук виждаме предупреждения за неопределени вградени функции, тоест за невъзможността да се свърже получаващият се бинарен файл. Изглежда, че в периода между 2006 година и този момент gcc е престанал да създава символи, използвани в вградените функции на междинно компилирани файлове. Нека опитаме да заменим ключовата дума inline с празен коментар и да видим резултата:

vi slirp.h
:6
a

#define inline /**/

:wq
make

Не. И това не работи. Все още не можем да намерим символите на тези функции.

На този етап се отказах и започнах да търся в Github пакети за компилиране на Heroku. Моята теория беше, че в някой от пакетите за компилиране на Heroku трябва да има нужните ми бинарни файлове. В крайна сметка търсенето ме доведе тук. Изтеглих и разархивирах uml.tar.gz и намерих следното:

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*

Това е бинарен файл slirp! А работи ли?

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

Copyright (c) 1995,1996 Danny Gasparovski и други.
Всички права запазени.
Тази програма е защитена с авторски права, безплатен софтуер.
Моля, прочетете файла COPYRIGHT, който е включен в пакета Slirp
за условията и условията на авторските права.

IP адрес на Slirp хоста: 127.0.0.1
IP адрес на вашите DNS(и): 1.1.1.1, 10.77.0.7
Вашият адрес е 10.0.2.15
(или нещо друго, което искате)

Напишете пет нули (0), за да излезете.

[автоопределяне SLIP/CSLIP, MTU 1500, MRU 1500]

SLiRP е готов ...

Не се срива — така че трябва да проработи! Нека пуснем този бинарен файл в ~\/bin\/slirp:

cp slirp ~\/bin\/slirp

В случай, че създателят на пакета го изтрие, аз направих огледало.

Настройка на мрежата

Сега нека настроим мрежата на нашето хоствано ядро. Актуализираме параметрите за стартиране:

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

Сега нека активираме мрежата:

mount -t 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

Първите две команди за конфигуриране /proc и /sys са необходими за работата ifconfig, която настройва мрежовия интерфейс за свързване със Slirp. Командата route настройва таблицата за маршрутизиране на ядрото, за да принуди целия трафик да минава през тунела Slirp. Нека проверим това с DNS запитване:

nslookup google.com 8.8.8.8
Сървър:    8.8.8.8
Адрес 1: 8.8.8.8 dns.google

Име:      google.com
Адрес 1: 172.217.12.206 lga25s63-in-f14.1e100.net
Адрес 2: 2607:f8b0:4006:81b::200e lga25s63-in-x0e.1e100.net

Работи!

Бел. ред.: Съдя по всичко, оригиналният пост е написан на десктоп с жична мрежова карта или с друга конфигурация, която не изисква допълнителни драйвери. На лаптоп с WiFi 8265 от Intel обаче при активиране на мрежата възниква грешка.

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

Очевидно, ядрото не може да се свърже с драйвера на мрежовото устройство. Опитът да се компилира firmware в ядрото, за съжаление, не поправи ситуацията. Към момента на публикуването в такава конфигурация решение не бе намерено. В по-прости конфигурации (например, във Virtualbox) интерфейсът се стартира коректно.

Нека автоматизираме пренасочването с помощта на следния 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

И ще го отбележим като изпълним:

chmod +x init.sh

А след това ще направим промени в командния ред на ядрото:

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

И ще повторим:

SLiRP Готово ...
мрежата е настроена
\/bin\/sh: не може да получи достъп до tty; контрол на работни места изключен

nslookup google.com 8.8.8.8
Сървър:    8.8.8.8
Адрес 1: 8.8.8.8 dns.google

Име:      google.com
Адрес 1: 172.217.12.206 lga25s63-in-f14.1e100.net
Адрес 2: 2607:f8b0:4004:800::200e iad30s09-in-x0e.1e100.net

Мрежата работи стабилно!

Docker файл

За да ви улесня с проверката, съставих Dockerfile, който автоматизира повечето от описаните стъпки и трябва да ви осигури работеща конфигурация. Имам и готова конфигурация на ядрото, в която има всичко, което е описано в поста. Важно е обаче да разберете, че тук изложих само минималната настройка.

Надявам се, че този пост ви е помогнал да разберете как да стартирате гостуващо ядро. Получи се някакъв монструм, но публикацията е замислена като всеобхватно ръководство по темата за компилиране, инсталиране и настройка на User Mode в Linux за съвременните версии на операционните системи от това семейство. Последващите действия трябва да включват инсталиране на услуги и друг софтуер вече в гостуващата система. Тъй като образите на Docker контейнери са просто разпаковани tar-архиви, трябва да можете да извлечете образа чрез docker export, а след това да определите пътя му за инсталиране в корена на файловата система на гостуващото ядро. След това изпълнете shell скрипта.

Специални благодарности на Rkeene от #lobsters в Freenode. Без неговата помощ в дебъгването на Slirp нямаше да стигна толкова далеч. Нямам представа как системата му Slackware работи коректно с slirp, но моите системи Ubuntu и Alpine не приеха slirp и предоставения ми бинарник от Rkeene. Но се радвам, че имам работещо нещо.

Източник: habr.com

Купете надежден хостинг за сайтове с защита от DDoS, VPS VDS сървъри 🔥 Купете надежден хостинг за сайтове с защита от DDoS, VPS VDS сървъри | ProHoster