![¿Cómo puedo reenviar puertos en QEMU en modo de usuario sin conflictos?](https://rvso.com/image/756237/%C2%BFC%C3%B3mo%20puedo%20reenviar%20puertos%20en%20QEMU%20en%20modo%20de%20usuario%20sin%20conflictos%3F.png)
Estoy ejecutando Linux bajo QEMU como parte de un conjunto de pruebas para un script de Python que realiza algunas operaciones privilegiadas. Ejecutar una máquina virtual completa es importante para mí porque:
- No quiero requerir acceso sudo para ejecutar pruebas
- Más adelante será más fácil ejecutar las pruebas en diferentes distribuciones y versiones del kernel.
- Es menos probable que se rompa mi computadora
Mi enfoque es iniciar la máquina virtual QEMU con un puerto de host reenviado a SSH en la máquina virtual, enviar y ejecutar el script a través de SSH, ejecutar el script y afirmar cosas sobre el sistema remoto. Lo molesto de esta configuración es el puerto SSH y los puertos para otras aplicaciones de red.
Mi pregunta:¿Cómo puedo reenviar puertos en QEMU en modo de usuario sin conflictos?
Mi objetivo aquí es poder ejecutar múltiples pruebas simultáneamente sin ninguna configuración previa y sin sudo, como se mencionó anteriormente. La red en modo de usuario en QEMU admite el reenvío de puertos, y cuando paso 0
por el puerto del host en la declaración hostfwd ( hostfwd=tcp:127.0.0.1:0-:22
), el sistema operativo asigna uno dinámicamente. ¡Impresionante! El problema con esto ha sido obtener ese puerto de manera confiable en el proceso principal.
Estoy haciendo el equivalente a:
#!/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
# ...
El sistema operativo invitado funciona bien.
Cosas que he probado:
jugó un poco conMPC, pero no pude encontrar un comando que proporcionara la información
Se consideró vincular un puerto aleatorio en el proceso principal
SO_REUSEPORT
y luego especificarlo para el comando secundario, pero QEMU no usa este indicador, por lo que no funciona.netstat -nalp | grep $child_pid
, pero esto no se podrá utilizar al reenviar múltiples puertoselegir un puerto independiente aleatorio, intentar iniciar qemu con él y volver a intentarlo si falla con un mensaje que coincida
Could not set up host forwarding rule 'tcp:...'
. Esto funciona, pero- es más probable que falle con más puertos
- puede ocultar otros problemas por los que no vale la pena volver a intentarlo
Es una alternativa aceptable, pero tengo la esperanza de una solución más sencilla.
Respuesta1
Después de investigar un poco más: QEMU expone la información a través del info usernet
comando. Así es como se ve el resultado:
(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
No está disponible a través de QMP, pero se envió un parche para agregar un equivalentequery-usernet
aquíhace tiempo.
En mi caso, es sencillo exponer el monitor usando -monitor unix:$PWD/mon.sock,server,nowait
, conectarlo, enviar el comando, leer el resultado y analizarlo para obtener los valores deseados.
En resumen, para usar QEMU para probar aplicaciones de red sin requerir sudo u otros requisitos previos (aparte del propio QEMU) y evitar conflictos de puertos en el host:
- Para cada aplicación, cree una asignación de puerto a través de la línea de comandos (
-netdev
) o monitor (netdev_add
), proporcionando0
el puerto del host. - Exponer
monitor
como un socket Unix para la aplicación consumidora - Ejecute
info usernet
a través del socket y extraiga el puerto de origen para cada asignación, que es el puerto real asignado por el sistema operativo.