如何在用户模式 ​​QEMU 中转发端口而不发生冲突?

如何在用户模式 ​​QEMU 中转发端口而不发生冲突?

我在 QEMU 下运行 Linux,作为执行某些特权操作的 Python 脚本的测试套件的一部分。运行完整的虚拟机对我来说很重要,因为:

  1. 我不想需要 sudo 访问权限来运行测试
  2. 以后将更容易在不同的发行版和内核版本上运行测试
  3. 我不太可能弄坏我的电脑

我的方法是启动 QEMU VM,并将主机端口转发到 VM 上的 SSH,通过 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,并提取每个映射的源端口,这是操作系统分配的实际端口

相关内容