![如何在使用者模式 QEMU 中轉送連接埠而不發生衝突?](https://rvso.com/image/756237/%E5%A6%82%E4%BD%95%E5%9C%A8%E4%BD%BF%E7%94%A8%E8%80%85%E6%A8%A1%E5%BC%8F%20QEMU%20%E4%B8%AD%E8%BD%89%E9%80%81%E9%80%A3%E6%8E%A5%E5%9F%A0%E8%80%8C%E4%B8%8D%E7%99%BC%E7%94%9F%E8%A1%9D%E7%AA%81%EF%BC%9F.png)
我在 QEMU 下運行 Linux,作為執行一些特權操作的 Python 腳本測試套件的一部分。運行完整的虛擬機器對我來說很重要,因為:
- 我不想需要 sudo 存取權限來運行測試
- 稍後跨不同發行版和核心版本運行測試將會更容易
- 我不太可能損壞我的電腦
我的方法是啟動 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
經過更多研究: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,這是作業系統分配的實際端口