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

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

このドキュメントでは、Hyper-V ソケット上に構築されるシンプルなプログラムを作成する手順について説明します。

サポートされるホスト OS

  • Windows 10 以降
  • Windows Server 2016 以降

サポートされるゲスト OS

Note

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

CONFIG_VSOCKET=y
CONFIG_HYPERV_VSOCKETS=y

機能と制限

  • カーネル モードまたはユーザー モード アクションをサポートする
  • データ ストリームのみ
  • ブロック メモリなし (バックアップ/ビデオには不適切)

作業の開始

要件:

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

注: Hyper-V ソケットの API は、Windows 10 Anniversary Update で公開されています。 HVSocket を使うアプリケーションは、あらゆる Widnows 10 ホストとゲストで実行できますが、開発には Windows SDK ビルド 14290 以降が必要です。

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

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 が表示されます。 これらは、Microsoft のインボックス サービスです。

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

  • 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

Note

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_VSOCKsvm_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 です。 VM の ID は次の PowerShell スニペットを使用して確認できます。

    (Get-VM -Name $VMName).Id
    
  • サービス ID - 上記の GUID。これによってアプリケーションは Hyper-V ホスト レジストリに登録されます。

接続が特定の仮想マシンに対する接続ではない場合は、利用できる VMID ワイルドカードのセットも存在します。

VMID ワイルドカード

Name 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()

HvSocket ソケット オプション

名前 種類 説明
HVSOCKET_CONNECTED_SUSPEND ULONG このソケット オプションが 0 以外の値に設定されている場合、仮想マシンが一時停止してもソケットは切断されません。

完全な WinSock API

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