独自の統合サービスを作成する

Windows 10 Anniversary Update以降、Hyper-V ソケット (新しいアドレス ファミリと仮想マシンを対象とした特殊なエンドポイントを備えた Windows ソケット) を使って Hyper-V ホストと仮想マシン間の通信を行うアプリケーションをだれでも作成できるようになりました。 Hyper-V を介したすべての通信はネットワークを使わずに実行され、すべてのデータは同じ物理メモリにとどまります。 Hyper-V ソケットを使うアプリケーションは、Hyper-V の統合サービスと似ています。

このドキュメントでは、Hyper-V ソケット上に単純なプログラムを構築する手順を説明します。

サポートされているホスト OS

  • Windows 10 以降
  • Windows Server 2016 以降

サポートされているゲスト OS

注意: サポートされる Linux ゲストでは、以下のコマンドに対応するカーネル サポートが必要です。

CONFIG_VSOCKET=y
CONFIG_HYPERV_VSOCKETS=y

機能と制限事項

  • カーネル モードまたはユーザー モード操作をサポート
  • データ ストリームのみ
  • ブロック メモリなし (バックアップ/ビデオに最適でない)

概要

要件:

  • C/C++ コンパイラ。 お持ちではない場合は、Visual Studio コミュニティを確認してください。
  • Windows 10 SDK: Visual Studio 2015 with Update 3 以降でプレインストールされています。
  • 上記のいずれかのホスト オペレーティング システムと 1 つ以上の仮想マシンが実行されているコンピューター。 これは、アプリケーションのテスト用です。

注: Hyper-v ソケットの API は、Windows 10 記念日更新プログラムで公開されます。 HVSocket を使うアプリケーションは、Windows 10 のすべてのホストとゲストで動作しますが、ビルド14290よりも後の Windows SDK でのみ開発できます。

新しいアプリケーションの登録

Hyper-V ソケットを使用するには、アプリケーションを Hyper-V ホストのレジストリに登録する必要があります。

レジストリにサービスを登録することで、次が得られます。

  • 使用可能なサービスを有効化、無効化、および一覧表示するための WMI 管理
  • 仮想マシンと直接通信するアクセス許可

次の PowerShell は、"HV Socket Demo" という名前の新しいアプリケーションを登録しています。 これは管理者として実行する必要があります。 手動の手順は次のとおりです。

$friendlyName = "HV Socket Demo"

# Create a new random GUID.  Add it to the services list
$service = New-Item -Path "HKLM:\SOFTWARE\Microsoft\Windows NT\CurrentVersion\Virtualization\GuestCommunicationServices" -Name ((New-Guid).Guid)

# Set a friendly name
$service.SetValue("ElementName", $friendlyName)

# Copy GUID to clipboard for later use
$service.PSChildName | clip.exe

レジストリの場所と情報:

HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\Windows NT\CurrentVersion\Virtualization\GuestCommunicationServices\

このレジストリの場所には、いくつかの GUID が表示されます。 これらは、マイクロソフトのボックスでのサービスです。

サービスごとのレジストリ内の情報。

  • Service GUID
    • ElementName (REG_SZ) - これは、サービスのフレンドリ名です。

独自のサービスを登録するには、独自の GUID とフレンドリ名を使用して新しいレジストリ キーを作成します。

フレンドリ名は、新しいアプリケーションに関連付けられます。 これは、GUID が適切でないパフォーマンス カウンターやその他の場所に表示されます。

レジストリ エントリは、次のようになります。

HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\Windows NT\CurrentVersion\Virtualization\GuestCommunicationServices\
    999E53D4-3D5C-4C3E-8779-BED06EC056E1\
        ElementName    REG_SZ    VM Session Service
    YourGUID\
        ElementName    REG_SZ    Your Service Friendly Name

注意: Linux ゲストのサービス GUID には、GUID ではなく svm_cid および svm_port によるアドレス指定が行われる VSOCK プロトコルが使用されます。 このような Windows との不整合を埋め合わせるために、よく知られている GUID がホスト上のサービス テンプレートとして使用され、ゲスト内のポートに変換されます。 サービス GUID をカスタマイズするには、先頭の "00000000" を必要なポート番号に変更します。 例: "00000ac9" は、ポート 2761 です。

// Hyper-V Socket Linux guest VSOCK template GUID
struct __declspec(uuid("00000000-facb-11e6-bd58-64006a7986d3")) VSockTemplate{};

 /*
  * GUID example = __uuidof(VSockTemplate);
  * example.Data1 = 2761; // 0x00000AC9
  */

ヒント: PowerShell で GUID を生成し、それをクリップボードにコピーするには、次のコマンドを実行します。

(New-Guid).Guid | clip.exe

Hyper-V ソケットの作成

最も基本的な例で、ソケットの定義には、アドレス ファミリ、接続の種類、およびプロトコルが必要です。

次に、簡単なソケット定義を示します。

// Windows
SOCKET WSAAPI socket(
  _In_ int af,
  _In_ int type,
  _In_ int protocol
);

// Linux guest
int socket(int domain, int type, int protocol);

Hyper-V ソケットの場合:

  • アドレス ファミリ - AF_HYPERV (Windows) または AF_VSOCK (Linux ゲスト)
  • タイプ - SOCK_STREAM
  • プロトコル - HV_PROTOCOL_RAW (Windows) または 0 (Linux ゲスト)

宣言/インスタンスの例を次に示します。

// Windows
SOCKET sock = socket(AF_HYPERV, SOCK_STREAM, HV_PROTOCOL_RAW);

// Linux guest
int sock = socket(AF_VSOCK, SOCK_STREAM, 0);

Hyper-V ソケットへのバインド

バインドは、ソケットと接続情報を関連付けます。

便宜上、下に関数定義をコピーしていますが、バインドの詳細についてはこちらをご覧ください。

// Windows
int bind(
  _In_ SOCKET                s,
  _In_ const struct sockaddr *name,
  _In_ int                   namelen
);

// Linux guest
int bind(int sockfd, const struct sockaddr *addr,
         socklen_t addrlen);

ホスト マシンの IP アドレスとそのホスト上のポート番号から構成される標準インターネット プロトコル アドレス ファミリ (AF_INET) のソケット アドレス (sockaddr) と対照的に、AF_HYPERV のソケット アドレスでは、仮想マシンの ID と上に定義したアプリケーション ID を使用して、接続を確立します。 Linux ゲストからのバインドの場合、AF_VSOCK では svm_cidsvm_port が使用されます。

Hyper-V ソケットは、ネットワーク スタック、TCP/IP、DNS などに依存しないため、ソケット エンドポイントには、引き続きあいまいに接続を記述する、ホスト名ではない非 IP 形式が必要です。

Hyper-V ソケットのソケット アドレスの定義を次に示します。

// Windows
struct SOCKADDR_HV
{
     ADDRESS_FAMILY Family;
     USHORT Reserved;
     GUID VmId;
     GUID ServiceId;
};

// Linux guest
// See include/uapi/linux/vm_sockets.h for more information.
struct sockaddr_vm {
    __kernel_sa_family_t svm_family;
    unsigned short svm_reserved1;
    unsigned int svm_port;
    unsigned int svm_cid;
    unsigned char svm_zero[sizeof(struct sockaddr) -
                   sizeof(sa_family_t) -
                   sizeof(unsigned short) -
                   sizeof(unsigned int) - sizeof(unsigned int)];
};

IP またはホスト名の代わりに、AF_HYPERV エンドポイントは 2 つの GUID に大きく依存します。

  • VM ID – これは、VM あたりに割り当てられた一意の ID です。 次の PowerShell サンプルを使用して、VM の ID を確認できます。
    (Get-VM -Name $VMName).Id
    
  • サービス ID - アプリケーションを Hyper-V ホスト レジストリに登録するための上記の GUID。

接続が特定の仮想マシンとの接続でない場合は、一連の VMID ワイルドカードを使用することもできます。

VMID ワイルドカード

名前 GUID 説明
HV_GUID_ZERO 00000000-0000-0000-0000-000000000000 リスナーは、すべてのパーティションからの接続を受け入れるために、この VmId にバインドする必要があります。
HV_GUID_WILDCARD 00000000-0000-0000-0000-000000000000 リスナーは、すべてのパーティションからの接続を受け入れるために、この VmId にバインドする必要があります。
HV_GUID_BROADCAST FFFFFFFF-FFFF-FFFF-FFFF-FFFFFFFFFFFF
HV_GUID_CHILDREN 90db8b89-0d35-4f79-8ce9-49ea0ac8b7cd 子のワイルドカード アドレス。 リスナーは、その子からの接続を受け入れるために、この VmId にバインドする必要があります。
HV_GUID_LOOPBACK e0e16197-dd56-4a10-9195-5ee7a155a838 ループバック アドレス。 この VmId を使用して、コネクタと同じパーティションに接続します。
HV_GUID_PARENT a42e7cda-d03f-480c-9cc2-a4de20abb878 親アドレス。 この VmId を使用して、コネクタの親パーティションに接続します。*

* HV_GUID_PARENT 仮想マシンの親は、その仮想マシンのホストです。 コンテナーの親は、コンテナーのホストです。 仮想マシンで実行しているコンテナーからの接続は、コンテナーをホストしている仮想マシンに接続します。 この VmId でのリッスンでは、次からの接続を受け入れることができます: (コンテナー内): コンテナー ホスト。 (VM 内: コンテナー ホスト/コンテナーなし): VM ホスト。 (VM 内でない: コンテナー ホスト/コンテナーなし): サポートされていません。

サポートされているソケット コマンド

Socket() Bind() Connect() Send() Listen() Accept()

完全な WinSock API

Hyper-V 統合サービスの参照