如何在使用者模式 QEMU 中轉發埠而不會發生衝突?
我在 QEMU 下執行 Linux,作為 Python 腳本測試套件的一部分,該腳本執行一些特權操作。執行完整的虛擬機對我來說很重要,因為:
- 我不想要求 sudo 訪問來執行測試
- 以後在不同的發行版和核心版本上執行測試會更容易
- 我不太可能弄壞我的電腦
我的方法是使用轉發到 VM 上的 SSH 的主機埠啟動 QEMU 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 # ...
來賓作業系統執行良好。
我嘗試過的事情:
玩了一點QMP,但找不到提供資訊的命令
考慮在父程序中綁定一個隨機埠,
SO_REUSEPORT
然後為子命令指定它,但是QEMU不使用這個標誌,所以它不起作用
netstat -nalp | grep $child_pid
,但是在轉發多個埠時這將不可用選擇一個隨機的未綁定埠,嘗試使用它啟動 qemu,如果失敗並顯示消息匹配,則再次嘗試
Could not set up host forwarding rule 'tcp:...'
。這有效,但是
- 更多埠更容易失敗
- 可能會掩蓋其他不值得重試的問題這是一個不錯的備份方案,但我對更直接的解決方案抱有希望。
經過一番研究: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 套接字- 通過socket執行
info usernet
,提取每個映射的Source Port,即OS實際分配的埠