Як пачаць выкарыстоўваць User Mode у Linux

Уступ ад перакладчыка: На фоне масавага ўваходу ў нашае жыццё рознага роду кантэйнераў можа быць даволі цікава і карысна даведацца, з якіх тэхналогій гэта ўсё пачыналася калісьці. Некаторыя з іх можна з карысцю ўжываць і дагэтуль, але не ўсё пра такія спосабы памятаюць (ці ведаюць, калі не заспелі падчас іх бурнага развіцця). Адной з такіх тэхналогій з'яўляецца User Mode Linux. Аўтар арыгінала ладна пакапалася, разбіраючыся, што са старых напрацовак яшчэ працуе, а што ўжо не вельмі, і сабрала нешта накшталт пакрокавай інструкцыі аб тым, як самому сабе завесці дамарослы UML у 2к19. І так, мы запрасілі на Хабр аўтара арыгінальнай пасады 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 сталі называць мяне чараўніцай (magic). Я спадзяюся, што гэты пост паслужыць камусьці надзейнай дакументацыяй для таго, каб пракруціць усё тое ж самае, але ўжо з навейшымі ядрамі і версіямі АС.

Настройка

Настройка Linux User Mode выконваецца ў некалькі этапаў:

  • усталёўка залежнасцяў на хасце;
  • спампоўка ядра Linux;
  • настройка зборкі ядра;
  • зборка ядра;
  • інсталяцыя бінарніка;
  • настройка гасцявой файлавай сістэмы;
  • падбор параметраў запуску ядра;
  • настройка гасцявой сеткі;
  • запуск гасцявога ядра.

Я мяркую, што калі вы вырашыце самастойна гэта ўсё пракруціць, хутчэй за ўсё, будзеце рабіць усё апісанае ў якой-небудзь Ubuntu ці Debian-падобнай сістэме. Я спрабавала рэалізаваць усё вышэйапісанае ў маім каханым дыстрыбутыве – Alpine, але нічога не выйшла, па ўсёй бачнасці, па чынніку таго, што ядро ​​Linux мае цвёрдую прывязку па glibc-isms для драйвераў у User Mode. Планую паведаміць пра гэта ў апстрым пасля таго, як канчаткова разбяруся ў праблеме.

Устаноўка залежнасцяў на хасце

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]', вы зможаце заняцца наладай з дапамогай клавіш Прабел або Увод. Навігацыя па акне, як звычайна, стрэлкамі клавіятуры "уверх" і "ўніз", а вылучэнне элементаў - "налева" або "направа".

Паказальнік выгляду -> азначае, што вы знаходзіцеся ў падменю, уваход у якое ажыццяўляецца клавішай Увод. Выйсце з яго, відавочна, праз '[Exit]».

Уключыце наступныя параметры ў '[Select]' і пераканайцеся, што побач з імі ёсць сімвал '[*]':

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

Усё, з гэтага акна можна выходзіць, паслядоўна выбіраючы '[Exit]'. Толькі пераканайцеся, што ў канцы вам прапануе захаваць канфігурацыю і вылучыце '[Yes]».

Я рэкамендую вам пагуляцца з параметрамі зборкі ядра пасля прачытання гэтай пасады. Дзякуючы гэтым эксперыментамі вы зможаце шматлікае запазычыць у плане разумення працы нізкаўзроўневых механік ядра і ўплывы розных сцягоў на яго зборку.

Зборка ядра

Ядро Linux - гэта вялікая праграма, якая займаецца мноствам рэчаў. Нават пры такой мінімальнай канфігурацыі на старым абсталяванні яго зборка можа заняць дастаткова часу. Таму зьбірайце ядро ​​з дапамогай наступнай каманды:

make ARCH=um -j$(nproc)

Навошта? Гэтая каманда скажа нашаму зборшчыку выкарыстоўваць усе даступныя ядры і патокі працэсара ў працэсе зборкі. Каманда $(nproc) у канцы Build падстаўляе выснову каманды 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 будзе вельмі складана. Але дадзенай ФС будзе дастаткова для ацэнкі агульнай ідэі.

Таксама нам спатрэбіцца інструмент ціні для спынення спажывання памяці зомбі-працэсамі нашага гасцявога ядра.

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
        available at http://user-mode-linux.sourceforge.net/

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

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

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

--help
    Prints this message.

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

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

--version
    Prints the version number of the kernel.

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

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

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

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

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

quiet
    Turns off information messages during boot.

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

Гэта палотнішча асвятляе асноўныя параметры запуску. Давайце запусцім ядро ​​з мінімальным неабходным наборам опцый:

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

Радкі вышэй кажуць нашаму ядру наступнае:

  • Выкажам здагадку, што каранёвая файлавая сістэма з'яўляецца псеўда-прыладай /dev/root.
  • выберы hostfs у якасці драйвера каранёвай файлавай сістэмы.
  • Змантуй гасцёўню файлавую сістэму, якую мы стварылі ў root-прыладзе.
  • І так, у рэжыме чытання-запісы.
  • Выкарыстоўвай толькі 64 мегабайта аператыўнай памяці (вы можаце выкарыстоўваць значна менш, у залежнасці ад таго, што вы плануеце рабіць, але 64 МБ здаюцца аптымальным аб'ёмам).
  • Ядро аўтаматычна запускае /bin/sh як init-працэс.

Запусціце гэтую каманду, і вы павінны атрымаць нешта накшталт наступнага:

Яшчэ адна прасціна

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

Маніпуляцыі вышэй дадуць нам гасцёўню сістэму на мінімалках, без такіх рэчаў, як /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 and others.
All rights reserved.
This program is copyrighted, free software.
Please read the file COPYRIGHT that came with the Slirp
package for the terms and conditions of the copyright.

IP address of Slirp host: 127.0.0.1
IP address of your DNS(s): 1.1.1.1, 10.77.0.7
Your address is 10.0.2.15
(or anything else you want)

Type five zeroes (0) to exit.

[autodetect SLIP/CSLIP, MTU 1500, MRU 1500, 115200 baud]

SLiRP Ready ...
fish: “/usr/bin/slirp” terminated by signal SIGSEGV (Address boundary error)

Ох, бажачкі. Давайце ўсталюем адладчык для 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.
License GPLv3+: GNU GPL version 3 or later <http://gnu.org/licenses/gpl.html>
This is free software: you are free to change and redistribute it.
There is NO WARRANTY, to the extent permitted by law.  Type "show copying"
and "show warranty" for details.
This GDB was configured as "x86_64-linux-gnu".
Type "show configuration" for configuration details.
For bug reporting instructions, please see:
<http://www.gnu.org/software/gdb/bugs/>.
Find the GDB manual and other documentation resources online at:
<http://www.gnu.org/software/gdb/documentation/>.
For help, type "help".
Type "apropos word" to search for commands related to "word"...
Reading symbols from /usr/bin/slirp...Reading symbols from /usr/lib/debug/.build-id/c6/2e75b69581a1ad85f72ac32c0d7af913d4861f.debug...done.
done.
(gdb) run
Starting program: /usr/bin/slirp
Slirp v1.0.17 (BETA)

Copyright (c) 1995,1996 Danny Gasparovski and others.
All rights reserved.
This program is copyrighted, free software.
Please read the file COPYRIGHT that came with the Slirp
package for the terms and conditions of the copyright.

IP address of Slirp host: 127.0.0.1
IP address of your DNS(s): 1.1.1.1, 10.77.0.7
Your address is 10.0.2.15
(or anything else you want)

Type five zeroes (0) to exit.

[autodetect SLIP/CSLIP, MTU 1500, MRU 1500, 115200 baud]

SLiRP Ready ...

Program received signal SIGSEGV, Segmentation fault.
                                                    ip_slowtimo () at ip_input.c:457
457     ip_input.c: No such file or directory.

Памылка б'ецца ў нас у гэтым радку. Давайце паглядзім на stacktrace, можа там нам што-небудзь дапаможа:

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

Тут мы бачым, што збой адбываецца падчас запуску асноўнага цыклу, калі 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
<enter>
#define inline /**/
<escape>
: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 and others.
All rights reserved.
This program is copyrighted, free software.
Please read the file COPYRIGHT that came with the Slirp
package for the terms and conditions of the copyright.

IP address of Slirp host: 127.0.0.1
IP address of your DNS(s): 1.1.1.1, 10.77.0.7
Your address is 10.0.2.15
(or anything else you want)

Type five zeroes (0) to exit.

[autodetect SLIP/CSLIP, MTU 1500, MRU 1500]

SLiRP Ready ...

Не падае - так што павінна спрацаваць! Давайце падсадзім гэты бінарнік у ~/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 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
Server:    8.8.8.8
Address 1: 8.8.8.8 dns.google

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

Працуе!

Заўв.пер.: Судзячы па ўсім, першапачатковы пост пісаўся на дэсктопе з правадной сеткавай картай, або нейкай іншай канфігурацыяй, не патрабавальнай дадатковых драйвераў. На наўтбуку з 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 Ready ...
networking set up
/bin/sh: can't access tty; job control turned off

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

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

Сетка працуе стабільна!

Докер-файл

Каб вам было прасцей усё гэта праверыць, я сабрала Докер-файл, які аўтаматызуе большасць апісаных крокаў і павінен забяспечыць вам працоўную канфігурацыю. Яшчэ ў мяне ёсць гатовая канфігурацыя ядра, у якой ёсць усё, што апісана ў пасце. Але важна разумець, што тут я выклала толькі мінімальную настройку.

Я спадзяюся, што гэты пост дапамог вам зразумець, як падняць госцевае ядро. Атрымаўся нейкі монстар, але задумвалася публікацыя ў якасці ўсёабдымнага кіраўніцтва на тэму зборкі, усталёўкі і налады User Mode у Linux пад сучаснымі версіямі аперацыйных сістэм гэтага сямейства. Наступныя дзеянні павінны складацца з усталёўку сэрвісаў і іншага праграмнага забеспячэння ўжо ўсярэдзіне гасцявой сістэмы. Так як вобразы кантэйнераў Docker - гэта проста распіяраныя tar-архівы, вы павінны быць у стане атрымаць вобраз праз docker export, а затым вызначыць шлях яго ўстаноўкі ў корані файлавай сістэмы гасцявога ядра. Ну, а затым выканайце shell-скрыпт.

Асобны дзякуй Rkeene з #lobsters на Freenode. Без яго дапамогі ў адладцы Slirp я не зайшла б так далёка. Я паняцця не маю, як яго сістэма Slackware карэктна працуе са slirp, але мае сістэмы Ubuntu і Alpine не прынялі slirp і прапанаваны мне Rkeene бінарнік. Але мне дастаткова і таго, што ў мяне працуе хоць нешта.

Крыніца: habr.com

Дадаць каментар