![Как перенаправить порты в пользовательском режиме QEMU без конфликтов?](https://rvso.com/image/756237/%D0%9A%D0%B0%D0%BA%20%D0%BF%D0%B5%D1%80%D0%B5%D0%BD%D0%B0%D0%BF%D1%80%D0%B0%D0%B2%D0%B8%D1%82%D1%8C%20%D0%BF%D0%BE%D1%80%D1%82%D1%8B%20%D0%B2%20%D0%BF%D0%BE%D0%BB%D1%8C%D0%B7%D0%BE%D0%B2%D0%B0%D1%82%D0%B5%D0%BB%D1%8C%D1%81%D0%BA%D0%BE%D0%BC%20%D1%80%D0%B5%D0%B6%D0%B8%D0%BC%D0%B5%20QEMU%20%D0%B1%D0%B5%D0%B7%20%D0%BA%D0%BE%D0%BD%D1%84%D0%BB%D0%B8%D0%BA%D1%82%D0%BE%D0%B2%3F.png)
Я запускаю Linux под QEMU как часть тестового набора для скрипта Python, который выполняет некоторые привилегированные операции. Запуск полной виртуальной машины важен для меня, потому что:
- Я не хочу требовать доступ sudo для запуска тестов.
- позже будет проще запускать тесты на разных дистрибутивах и версиях ядра
- У меня меньше шансов сломать компьютер
Мой подход заключается в запуске 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
После небольшого исследования: 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) и избегая конфликтов портов на хосте:
- Для каждого приложения создайте сопоставление портов через командную строку (
-netdev
) или монитор (netdev_add
), указав0
порт хоста - Предоставить
monitor
как сокет unix для потребляющего приложения - Выполнить
info usernet
через сокет и извлечь исходный порт для каждого сопоставления, который является фактическим портом, выделенным ОС.