ユーザーモード QEMU で競合なくポートを転送するにはどうすればよいですか?

ユーザーモード QEMU で競合なくポートを転送するにはどうすればよいですか?

私は、特権操作を実行する Python スクリプトのテスト スイートの一部として、QEMU で Linux を実行しています。完全な仮想マシンを実行することは、私にとって重要です。その理由は次のとおりです。

  1. テストを実行するためにsudoアクセスを必要としたくない
  2. 後で異なるディストリビューションやカーネルバージョン間でテストを実行するのが容易になります。
  3. コンピュータを壊す可能性が低くなる

私のアプローチは、VM 上の SSH に転送されたホスト ポートで QEMU VM を起動し、SSH 経由でスクリプトを送信して実行し、スクリプトを実行して、リモート システムに関する情報をアサートすることです。この設定で厄介なのは、SSH ポートと他のネットワーク アプリケーションのポートです。

私の質問:ユーザーモード QEMU で競合なくポートを転送するにはどうすればよいですか?

ここでの私の目標は、前述のように、事前の設定や sudo なしで複数のテストを同時に実行できるようにすることです。QEMU のユーザー モード ネットワークはポート転送をサポートしており、0hostfwd 宣言 ( ) でホスト ポートを渡すと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. ポート数が増えると失敗する可能性が高くなります
    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私の場合、 を使用してモニターを公開し、接続し、コマンドを送信し、出力を読み取り、目的の値を解析するのは簡単です。

まとめると、sudo やその他の前提条件 (QEMU 自体以外) を必要とせず、ホスト上のポート競合を回避しながら QEMU を使用してネットワーク アプリケーションをテストするには、次の手順を実行します。

  1. 各アプリケーションについて、コマンドライン(-netdev)またはモニター(netdev_add)を介してポートマッピングを作成し、0ホストポートを指定します。
  2. monitorを消費アプリケーション用のUNIXソケットとして公開する
  3. info usernetソケット経由で実行し、各マッピングのソースポート(OSによって割り当てられた実際のポート)を抽出します。

関連情報