Spice Vdagent protocol

Spice Guest Agent is an important component that runs inside the Guest OS to enhance the performance of the spice protocol. Mainly realized: mouse client mode, automatic resolution adjustment, clipboard copy, file drag and drop and other important functions. If there is no dagent program, then the spice protocol medical examination is unacceptable.

vdgent program

  • Use the serial port on Linux:/dev/virtio-ports/com.redhat.spice.0
  • Use serial port on Windows:\\\\.\\Global\\com.redhat.spice.0
  • qemu startup parameters:device virtio-serial-pci,id=virtio-serial0,max_ports=16,bus=pci.0,addr=0x5 -chardev spicevmc,name=vdagent,id=vdagent -device virtserialport,nr=1,bus=virtio-serial0.0,chardev=vdagent,name=com.redhat.spice.0

Protocol header

1.VDIChunkHeader

vdagent connects to the spice server through the virtio serial port, can send data to the spice server and spice client, and can also receive the data sent by them. VDIChunkHeader is used as the message header.

typedef struct SPICE_ATTR_PACKED VDIChunkHeader {
  uint32_t port; //vdagent接收消息时:port是消息来源,dagent发送消息时:port是接收方
  uint32_t size; //size是发送或接收到的数据的大小,包括可变数据部分的大小, size=sizeof(VDAgentMessage)+variable_data_len
} VDIChunkHeader;

Value of element port:

enum {
    VDP_CLIENT_PORT = 1,
    VDP_SERVER_PORT,
};

When the message to be sent is a response message to a received message, the port value should be consistent with the port value in the received message.

When the spice server receives a message, it will judge the port data in the VDIChunkHeader to decide whether to send the data directly to the client or process it by itself (if it is an invalid value, discard it). There is no message from the Agent for the spice server, so all messages with the port value of VDP_SERVER_PORT will be silently discarded by the server.

2.VDAgentMessage

The messages sent or received by the Agent are encapsulated in a structure called VDAgentMessage:

typedef struct SPICE_ATTR_PACKED VDAgentMessage {
  uint32_t protocol; //固定值VD_AGENT_PROTOCOL
  uint32_t type; //消息类型,在一个enum中
  uint64_t opaque; //opaque是消息类型的占位符,只需要将单个整数作为消息数据传递,对于具有更多数据的消息类型,opaque值始终设置为0.
  uint32_t size; // 可变长度数据的大小,值为:sizeof(VDIChunkHeader) + sizeof(VDAgentMessage) + variable_data_len.
  uint8_t data[0]; //可变长度数据的起始地址,可变长度数据的内容取决于数据类型,对于大多数消息,它是一种数据类型特定的结构体,如VDAgentMouseSate。
} VDAgentMessage;

Message type: the type field of VDAgentMessage, each element represents a protocol

enum {
    VD_AGENT_MOUSE_STATE = 1,
    VD_AGENT_MONITORS_CONFIG,
    VD_AGENT_REPLY,
    VD_AGENT_CLIPBOARD,
    VD_AGENT_DISPLAY_CONFIG,
    VD_AGENT_ANNOUNCE_CAPABILITIES,
    VD_AGENT_CLIPBOARD_GRAB,
    VD_AGENT_CLIPBOARD_REQUEST,
    VD_AGENT_CLIPBOARD_RELEASE,
    VD_AGENT_FILE_XFER_START,
    VD_AGENT_FILE_XFER_STATUS,
    VD_AGENT_FILE_XFER_DATA,
    VD_AGENT_CLIENT_DISCONNECTED,
    VD_AGENT_MAX_CLIPBOARD,
    VD_AGENT_END_MESSAGE,
};

Agreement details

1. Mouse mode (VD_AGENT_MOUSE_STATE)

spice server ---> vdagent

typedef struct SPICE_ATTR_PACKED VDAgentMouseState {
  uint32_t x;
  uint32_t y;
  uint32_t buttons;
  uint8_t display_id;
} VDAgentMouseState;

Spice supports two mouse modes, server and client.
In server mode, QEMU ps/2 mouse emulation is used to send the mouse status to the guest. When the mouse enters the client window, the mouse is captured, and the client sends the incremental coordinates of the mouse movement to the client (Guest). The effect is extremely poor.
In client mode, the coordinate position of the mouse in the display is sent to the client as an absolute value. This requires emulation using usb tables, or sending them to vdagent, and vdagent will notify the guest operating system of the mouse position (and button click).

When the mouse is in the client mode, the spice server sends the VDAgentMouseState message to the vdagent through the agent protocol VD_AGENT_MOUSE_STATE, and the vagent adjusts the mouse position of the guest os.

Note: These messages are sent by spice server, not by spice client, because spice server performs all mouse processing (such as switching client mode or server mode when vdagent is connected/disconnected).

2. Display configuration (VD_AGENT_MONITORS_CONFIG)

spice client ---> vdagent

typedef struct SPICE_ATTR_PACKED VDAgentMonitorsConfig {
  uint32_t num_of_monitors;
  uint32_t flags;
  VDAgentMonConfig monitors[0];
} VDAgentMonitorsConfig

Immediately following the message are num_of_moniters the following structures:

typedef struct SPICE_ATTR_PACKED VDAgentMonConfig {
  uint32_t height;
  uint32_t width;
  uint32_t depth;
  int32_t x;
  int32_t y;
} VDAgentMonConfig;

When the client is running in full-screen auto-configuration mode, the client will send this message to vdagent. This message contains information about the monitor connected to the client computer. After receiving this message, vdagent should reconfigure the output of the qxl vga device in the guest to match the output in the message as much as possible.

When the vdagent completes the configuration, the agent should return a VD_AGENT_REPLY message. The type value in the message is set to VD_AGENT_MONITORS_CONFIG, and the error value is set to VD_AGENT_SUCCESS or VD_AGENT_ERROR to indicate whether the configuration is successful or incorrect.

3. Message response (VD_AGENT_REPLY)

vdagent ---> spice client

typedef struct SPICE_ATTR_PACKED VDAgentReply {
  uint32_t type;
  uint32_t error;
} VDAgentReply;
其中:error值为:
enum {
  VD_AGENT_SUCCESS = 1,
  VD_AGENT_ERROR,
};

This message is sent by vdagent to the client, indicating that vdagent has finished processing the VD_AGENT_MONITORS_CONFIG or VD_AGENT_DISPLAY_CONFIG message, and whether the processing succeeded or failed.

4. Notify yourself of supported functions (VD_AGENT_ANNOUNCE_CAPABILITIES)

spice client ---> vdagent
vdagent ---> spice client
This protocol is notified by spice client and vdagent of the functions they support. The purpose of this message is to allow different versions of the client and vdagent to work together.

typedef struct SPICE_ATTR_PACKED VDAgentAnnounceCapabilities {
  uint32_t  request; 
  uint32_t caps[0];
} VDAgentAnnounceCapabilities;

The request field is a Boolean value, indicating whether the receiver of the message needs to reply with a VD_AGENT_ANNOUNCE_CAPABILITIES message, because the sender may also want to know the capabilities of the receiver. This should be set to true when the capabilities exchange is initiated at the very beginning. When a declaration of capabilities is sent as a response to the received information, the value is set to false.

The caps member is the first address of a variable-length array. The length of the array can be determined using the VD_AGENT_CAPS_SIZE_FROM_MSG_SIZE macro on the size member of the VDAgentMessage structure. The indexes of different capabilities are located in the enumeration defined in the VD_AGENT_CAP constant. And there are VD_AGENT_HAS_CAPABILITY and VD_AGENT_SET_CAPABILITY macros to test/set the capability bit in the array.

5. Display effect optimization (VD_AGENT_DISPLAY_CONFIG)

spice client ---> vdagent

typedef struct SPICE_ATTR_PACKED VDAgentDisplayConfig {
  uint32_t flags;
  uint32_t depth;
} VDAgentDisplayConfig;

enum {
  VD_AGENT_DISPLAY_CONFIG_FLAG_DISABLE_WALLPAPER = (1 << 0),
  VD_AGENT_DISPLAY_CONFIG_FLAG_DISABLE_FONT_SMOOTH = (1 << 1),
  VD_AGENT_DISPLAY_CONFIG_FLAG_DISABLE_ANIMATION = (1 << 2),
  VD_AGENT_DISPLAY_CONFIG_FLAG_SET_COLOR_DEPTH = (1 << 3),
};

The spice client sends to vdagent to notify vdagent of some special performance related settings. The client can ask vdagent to disable many features of the client system, such as font anti-aliasing, to improve performance. vdagent can do some effort in these areas, especially because most of the settings are windows-centric. VD_AGENT_REPLY should be used to return a success status unless there is a problem.

6. Clipboard related agreements

The agent and the client on the client play a symmetrical role: they can both claim ownership (GRAB), release ownership (RELEASE), request clipboard data (REQUEST), and send clipboard data. When the clipboard is emptied, the grab information must be released.

The other end can request data when the grab is active, and then will receive a CLIPBOARD message with clipboard information from the other party.

The clipboard data types are as follows:

enum {
  VD_AGENT_CLIPBOARD_NONE = 0,
  VD_AGENT_CLIPBOARD_UTF8_TEXT,
  VD_AGENT_CLIPBOARD_IMAGE_PNG,  /* All clients with image support should support this one */
  VD_AGENT_CLIPBOARD_IMAGE_BMP,  /* optional */
  VD_AGENT_CLIPBOARD_IMAGE_TIFF, /* optional */
  VD_AGENT_CLIPBOARD_IMAGE_JPG,  /* optional */
};

If both parties implement the function of VD_AGENT_CAP_CLIPBOARD_SELECTION, the clipboard information must have a uint8_t value in front of it, indicating the clipboard selection to be operated.

VD_AGENT_CLIPBOARD
struct VDAgentClipboard {
if VD_AGENT_CAP_CLIPBOARD_SELECTION capability
  uint8_t selection;
  uint8_t __reserved[3];
endif
  uint32_t type; //type值是剪贴板数据类型,即前面剪贴板数据枚举类型中的一个。
  uint8_t data[0]; //可变长度数据的起始地址
};

VD_AGENT_CLIPBOARD is used to send clipboard data. Unless a data request of VD_AGENT_CLIPBOARD_REQUEST is received, this data will not be sent to avoid wasting bandwidth. The clipboard data to be transmitted is usually quite large. In this case, it can be expected that the data to be transmitted will be divided into multiple VD_AGENT_MESSAGEs and sent.

VD_AGENT_CLIPBOARD_REQUEST
struct VDAgentClipboardRequest {
if VD_AGENT_CAP_CLIPBOARD_SELECTION capability
  uint8_t selection;
  uint8_t __reserved[3];
endif
  uint32_t type; //剪贴板数据类型,即前面剪贴板数据枚举类型中的一个。
};

Request clipboard data of the specified type.

VD_AGENT_CLIPBOARD_GRAB
struct VDAgentClipboardGrab {
if VD_AGENT_CAP_CLIPBOARD_SELECTION capability
  uint8_t selection;
  uint8_t __reserved[3];
endif
  uint32_t types[0];
};

Grab clipboard data. Any fetch type data request should be successful.

VD_AGENT_CLIPBOARD_RELEASE
struct VDAgentClipboardRelease {
if VD_AGENT_CAP_CLIPBOARD_SELECTION capability
  uint8_t selection;
  uint8_t __reserved[3];
endif
};

Release the clipboard. Note: If a GRAB message has been sent and is currently active, and then continuous GRAB messages are received from the other party, you should not send a RELEASE message to the other party to cancel the previous grab. Because the active grab has been secretly released by the opponent. If you send additional RELEASE information to the other party, it will only confuse the other party.

VD_AGENT_CAP_CLIPBOARD_SELECTION

When both the client and the server have selection capability, the VDAgentClipboard information must be preceded by a uint8_t, which indicates which clipboard selection can operate + 3 bytes to fill for future functions or extensions.

Several clipboard options are defined according to the X11/Gtk scheme:

  • (1) VD_AGENT_CLIPBOARD_SELECTION_CLIPBOARD: The default clipboard, implemented by most operating systems to handle explicit copy/paste operations.
  • (2) VD_AGENT_CLIPBOARD_SELECTION_PRIMARY: PRIMARY clipboard, used for mouse selection.
  • (3)VD_AGENT_CLIPBOARD_SELECTION_SECONDARY: the SECONDARY clipboard.

Take spice client ---> vdagent as an example:

  • The spice client sends a VD_AGENT_CLIPBOARD_GRAB message to tell the spice client that the data has been changed.
  • vdagent receives the VD_AGENT_CLIPBOARD_GRAB message, sets the clipboard owner to the spice client, and sends the VD_AGENT_CLIPBOARD_REQUEST message to the spice client
  • The spice client receives the D_AGENT_CLIPBOARD_REQUEST message and sends back the packet with: VDagentClipboard structure data
  • vdagent receives the DagentClipboard structure, gets the clipboard data, and updates the clipboard of guest os

7. File transfer related protocols

The spice protocol supports dragging and dropping files from the spice client to the virtual machine. However, reverse operation is not supported.

VD_AGENT_FILE_XFER_START
typedef struct SPICE_ATTR_PACKED VDAgentFileXferStartMessage {
   uint32_t id;
   uint8_t data[0];
} VDAgentFileXferStartMessage;

VD_AGENT_FILE_XFER_STATUS

typedef struct SPICE_ATTR_PACKED VDAgentFileXferStatusMessage {
   uint32_t id;
   uint32_t result;
   /* Used to send additional data for detailed error messages
    * to clients with VD_AGENT_CAP_FILE_XFER_DETAILED_ERRORS capability.
    * Type of data varies with the result:
    * result : data type (NULL if no data)
    * VD_AGENT_FILE_XFER_STATUS_NOT_ENOUGH_SPACE : uint64_t
    * VD_AGENT_FILE_XFER_STATUS_SESSION_LOCKED : NULL
    * VD_AGENT_FILE_XFER_STATUS_VDAGENT_NOT_CONNECTED : NULL
    * VD_AGENT_FILE_XFER_STATUS_DISABLED : NULL
    */
   uint8_t data[0];
} VDAgentFileXferStatusMessage;

VD_AGENT_FILE_XFER_DATA

typedef struct SPICE_ATTR_PACKED VDAgentFileXferDataMessage {
   uint32_t id;
   uint64_t size;
   uint8_t data[0];
} VDAgentFileXferDataMessage;

Guess you like

Origin blog.51cto.com/14207158/2675202