如何在使用者模式 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. 透過socket執行info usernet,並提取每個映射的Source Port,這是作業系統分配的實際端口

相關內容