![ユーザーモード QEMU で競合なくポートを転送するにはどうすればよいですか?](https://rvso.com/image/756237/%E3%83%A6%E3%83%BC%E3%82%B6%E3%83%BC%E3%83%A2%E3%83%BC%E3%83%89%20QEMU%20%E3%81%A7%E7%AB%B6%E5%90%88%E3%81%AA%E3%81%8F%E3%83%9D%E3%83%BC%E3%83%88%E3%82%92%E8%BB%A2%E9%80%81%E3%81%99%E3%82%8B%E3%81%AB%E3%81%AF%E3%81%A9%E3%81%86%E3%81%99%E3%82%8C%E3%81%B0%E3%82%88%E3%81%84%E3%81%A7%E3%81%99%E3%81%8B%3F.png)
私は、特権操作を実行する Python スクリプトのテスト スイートの一部として、QEMU で Linux を実行しています。完全な仮想マシンを実行することは、私にとって重要です。その理由は次のとおりです。
- テストを実行するためにsudoアクセスを必要としたくない
- 後で異なるディストリビューションやカーネルバージョン間でテストを実行するのが容易になります。
- コンピュータを壊す可能性が低くなる
私のアプローチは、VM 上の SSH に転送されたホスト ポートで QEMU VM を起動し、SSH 経由でスクリプトを送信して実行し、スクリプトを実行して、リモート システムに関する情報をアサートすることです。この設定で厄介なのは、SSH ポートと他のネットワーク アプリケーションのポートです。
私の質問:ユーザーモード QEMU で競合なくポートを転送するにはどうすればよいですか?
ここでの私の目標は、前述のように、事前の設定や sudo なしで複数のテストを同時に実行できるようにすることです。QEMU のユーザー モード ネットワークはポート転送をサポートしており、0
hostfwd 宣言 ( ) でホスト ポートを渡すとhostfwd=tcp:127.0.0.1:0-:22
、OS がポートを動的に割り当てます。すばらしい! 問題は、親プロセスでそのポートを確実に取得することです。
私がやっているのは、以下のことと同等のことです:
#!/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
# ...
ゲスト OS は正常に起動します。
私が試したこと:
少し遊んだ品質管理、しかし、情報を提供するコマンドは見つかりませんでした
親プロセスでランダムなポートをバインドし
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
私の場合、 を使用してモニターを公開し、接続し、コマンドを送信し、出力を読み取り、目的の値を解析するのは簡単です。
まとめると、sudo やその他の前提条件 (QEMU 自体以外) を必要とせず、ホスト上のポート競合を回避しながら QEMU を使用してネットワーク アプリケーションをテストするには、次の手順を実行します。
- 各アプリケーションについて、コマンドライン(
-netdev
)またはモニター(netdev_add
)を介してポートマッピングを作成し、0
ホストポートを指定します。 monitor
を消費アプリケーション用のUNIXソケットとして公開するinfo usernet
ソケット経由で実行し、各マッピングのソースポート(OSによって割り当てられた実際のポート)を抽出します。