Как перенаправить порты в пользовательском режиме QEMU без конфликтов?

Как перенаправить порты в пользовательском режиме QEMU без конфликтов?

Я запускаю Linux под QEMU как часть тестового набора для скрипта Python, который выполняет некоторые привилегированные операции. Запуск полной виртуальной машины важен для меня, потому что:

  1. Я не хочу требовать доступ sudo для запуска тестов.
  2. позже будет проще запускать тесты на разных дистрибутивах и версиях ядра
  3. У меня меньше шансов сломать компьютер

Мой подход заключается в запуске QEMU VM с хост-портом, перенаправленным на SSH на VM, отправке и запуске скрипта по SSH, запуске скрипта и утверждении информации об удаленной системе. Раздражает в этой настройке порт SSH и порты для других сетевых приложений.

Мой вопрос:Как перенаправить порты в пользовательском режиме QEMU без конфликтов?

Моя цель здесь — иметь возможность запускать несколько тестов одновременно без какой-либо предварительной настройки и без sudo, как упоминалось выше. Сетевой режим пользователя в QEMU поддерживает переадресацию портов, и когда я передаю 0порт хоста в объявлении hostfwd ( hostfwd=tcp:127.0.0.1:0-:22), ОС выделяет его динамически. Потрясающе! Проблема с этим заключалась в надежном получении этого порта в родительском процессе.

Я делаю что-то похожее на:

#!/bin/sh
set -e

cd "$(mktemp -d)"

# Download cloud image.
image_base_url="https://cloud-images.ubuntu.com/releases/18.04/release/"
image_name="ubuntu-18.04-server-cloudimg-amd64.img"
image_url="$image_base_url$image_name"
curl --location -o linux.img $image_url

# Create SSH key.
ssh_key_path="$PWD/id_rsa"
ssh-keygen -t rsa -b 4096 -N "" -f "$ssh_key_path"

# Create cloud-init payload.
cat <<EOF > user_data
#cloud-config
password: ubuntu
chpasswd: { expire: False }
ssh_pwauth: True
ssh_authorized_keys:
- $(cat "${ssh_key_path}.pub")
EOF
cloud-localds user_data.img user_data

# Socket path for QMP.
qmp_socket="$PWD/qmp.sock"

# Start VM.
qemu-system-x86_64 \
    -drive file=linux.img,format=qcow2 \
    -drive file=user_data.img,format=raw \
    -netdev user,id=net0,hostfwd=tcp:127.0.0.1:0-:22 \
    -device rtl8139,netdev=net0 \
    -enable-kvm \
    -m 2G \
    -serial mon:stdio \
    -nographic \
    -smp 2 \
    -snapshot \
    -qmp unix:$qmp_socket,server,nowait \
    & \
;

# wait a bit, then test stuff here
# ...

Гостевая ОС запускается нормально.

Что я пробовал:

  • немного поиграл сКМП, но не удалось найти команду, которая предоставила бы информацию

  • рассматривалась возможность привязки случайного порта в родительском процессе с SO_REUSEPORTпоследующим указанием его для дочерней команды, но QEMU не использует этот флаг, поэтому это не работает

  • netstat -nalp | grep $child_pid, но это не будет применимо при переадресации нескольких портов

  • выбираем случайный непривязанный порт, пытаемся запустить qemu с ним и пробуем снова, если это не удается с сообщением, соответствующим Could not set up host forwarding rule 'tcp:...'. Это работает, но

    1. больше вероятность выхода из строя при большем количестве портов
    2. может скрыть другие проблемы, которые не стоит пытаться решить повторно

    Это неплохой запасной вариант, но я надеюсь на более простое решение.

решение1

После небольшого исследования: QEMU раскрывает информацию через info usernetкоманду. Вот как выглядит вывод:

(qemu) info usernet
VLAN -1 (net0):
  Protocol[State]    FD  Source Address  Port   Dest. Address  Port RecvQ SendQ
  TCP[HOST_FORWARD]  13       127.0.0.1 38117       10.0.2.15    22     0     0
  UDP[236 sec]       24       10.0.2.15 35061   91.189.89.198   123     0     0
  UDP[204 sec]       26       10.0.2.15 60630   91.189.89.198   123     0     0

Он недоступен через QMP, но был представлен патч для добавления эквивалента.query-usernet здесьнекоторое время назад.

В моем случае это просто: отобразить монитор с помощью -monitor unix:$PWD/mon.sock,server,nowait, подключиться, отправить команду, прочитать вывод и проанализировать его на предмет нужных значений.

Итак, подведем итог: чтобы использовать QEMU для тестирования сетевых приложений без необходимости использования sudo или других предварительных требований (кроме самого QEMU) и избегая конфликтов портов на хосте:

  1. Для каждого приложения создайте сопоставление портов через командную строку ( -netdev) или монитор ( netdev_add), указав 0порт хоста
  2. Предоставить monitorкак сокет unix для потребляющего приложения
  3. Выполнить info usernetчерез сокет и извлечь исходный порт для каждого сопоставления, который является фактическим портом, выделенным ОС.

Связанный контент