Features/GuestAgent: Difference between revisions
(Created page with '== Summary == Implement support for QMP commands and events that terminate and originate respectively within the guest using an agent built as part of QEMU. == Detailed Summary…') |
No edit summary |
||
Line 1: | Line 1: | ||
== Summary == | == Summary == | ||
Implement support for QMP commands and events that terminate and originate respectively within the guest using an agent built as part of QEMU. | Implement support for QMP commands and events that terminate and originate | ||
respectively within the guest using an agent built as part of QEMU. | |||
== Detailed Summary == | == Detailed Summary == | ||
There are two proposed ways to implement support for a guest agent today in QEMU both with advantages and trade-offs. | There are two proposed ways to implement support for a guest agent today in | ||
QEMU both with advantages and trade-offs. | |||
One approach takes QEMU completely out of the picture by exposing a guest agent over a transport (like virtio-serial) and then exposing that transport over a CharDriverState to be terminated in the management layer (ala libvirt). This model keeps complexity out of QEMU but exposes management tools directly to the guest potentially significantly increases the guest's attack surface. In addition, it makes live migration very challenging to coordinating while keeping communication transparent to the guest agent. | One approach takes QEMU completely out of the picture by exposing a guest agent | ||
over a transport (like virtio-serial) and then exposing that transport over a | |||
CharDriverState to be terminated in the management layer (ala libvirt). This | |||
model keeps complexity out of QEMU but exposes management tools directly to the | |||
guest potentially significantly increases the guest's attack surface. In | |||
addition, it makes live migration very challenging to coordinating while | |||
keeping communication transparent to the guest agent. | |||
Another proposal terminates the guest agent directly within QEMU. This introduces considerable complexity into QEMU by creating a new RPC transport potentially increasing the guest attack surface. While it does improve support for live migration, it requires a parallel API within QMP to support management tools. | Another proposal terminates the guest agent directly within QEMU. This | ||
introduces considerable complexity into QEMU by creating a new RPC transport | |||
potentially increasing the guest attack surface. While it does improve support | |||
for live migration, it requires a parallel API within QMP to support management | |||
tools. | |||
A potential middle ground is to adopt QMP as the RPC interface between the guest agent and QEMU and expose the guest commands as a QMP namespace. This eliminates the need for a parallel API, addresses live migration robustly, and by reusing the existing QMP infrastructure, avoids significantly increasing the guest attack surface. | A potential middle ground is to adopt QMP as the RPC interface between the | ||
guest agent and QEMU and expose the guest commands as a QMP namespace. This | |||
eliminates the need for a parallel API, addresses live migration robustly, and | |||
by reusing the existing QMP infrastructure, avoids significantly increasing the | |||
guest attack surface. | |||
== | == Schema Definition == | ||
All guest commands will use a ''guest-'' prefix to distinguish the fact that the commands are handled by the guest. Likewise, events will carry a ''GUEST_'' prefix. Type names (complex types and enums) do not require a special prefix. The following is an example of the proposed guest agent schema: | All guest commands will use a ''guest-'' prefix to distinguish the fact that | ||
the commands are handled by the guest. Likewise, events will carry a | |||
''GUEST_'' prefix. Type names (complex types and enums) do not require a | |||
special prefix. The following is an example of the proposed guest agent schema: | |||
// qemu/qmp-schema.json | // qemu/qmp-schema.json | ||
[ 'guest-ping', {}, 'none' ] | [ 'guest-ping', {}, 'none' ] | ||
[ 'guest-view-file', {'filename', 'str'}, ' | [ 'guest-view-file', {'filename', 'str'}, 'str' ] | ||
{ 'GuestType': [ 'linux', 'windows', 'freebsd', 'netbsd', 'openbsd', 'solaris' ] } | { 'GuestType': [ 'linux', 'windows', 'freebsd', 'netbsd', | ||
{ 'GuestInfo': { 'os-type': 'GuestType', 'os-version': 'str', 'agent-version': 'str' } } | 'openbsd', 'solaris' ] } | ||
{ 'GuestInfo': { 'os-type': 'GuestType', 'os-version': 'str', | |||
'agent-version': 'str' } } | |||
{ 'GUEST_STARTUP': { 'GuestInfo': 'str' } } | { 'GUEST_STARTUP': { 'GuestInfo': 'str' } } | ||
== libqmp == | |||
In libqmp, the code generated for a guest command is identical to the code | |||
generated for a normal command. | |||
For instance, the ''guest-view-file'' command will have the following signature: | |||
char *qmp_guest_view_file(QmpSession *sess, const char *filename, Error **errp); | |||
== QEMU == | |||
The only role QEMU plays in guest commands is unmarshalling and remarshalling | |||
the input and output. This means that data from the guest is not being sent | |||
directly to a management tool which significantly decreases the guest attack | |||
surface. | |||
This works by combining the existing unmarshalling code for the QMP server with | |||
the marshalling code from libqmp. Here is an example of the code that will be | |||
generated: | |||
void qmp_guest_ping(Error **errp) | |||
{ | |||
QDict *args = qdict_new(); | |||
QObject *ret_data; | |||
// no arguments to marshal | |||
ret_data = qmp_guest_command_dispatch("guest-ping", args, errp); | |||
// ret_data should always be empty | |||
qobject_decref(ret_data); | |||
QDECREF(args); | |||
} | |||
static void qmp_marshal_guest_ping(const QDict *args, QObject **ret_data, Error **errp) | |||
{ | |||
// no arguments to unmarshal | |||
qmp_guest_ping(errp); | |||
// no retval to marshal | |||
} | |||
== virtio-serial Transport == | |||
The ''qmp_guest_command_dispatch'' command will take the QObjects and generate | |||
a QMP command to send to the guest. This will be sent to the guest via a new | |||
CharDriverState implementation. This CharDriverState will provide a backend | |||
to virtio-serial. It will essentially act as an in-memory chardev except that | |||
it will parse the input from the guest for invalid UTF-8 characters. If an | |||
invalid character is detected, the CharDriverState will generate a reset. | |||
This behavior will be utilized by the guest in order to reset the QMP session | |||
after the guest agent restarts. The first byte it writes to virtio-serial will | |||
always be 0xFF. | |||
== Guest Agent == | |||
The guest agent will be a daemon that connects to a virtio-serial device and | |||
feeds the input to a JSON parser. When a new command is received, it will hand | |||
the command over to the QAPI generated dispatch routines. | |||
The guest agent will implement the server side of the QMP commands using the | |||
native signature for the function. | |||
== Asynchronous Commands == | |||
Since QEMU cannot rely on the guest agent responding immediately to a command | |||
(it is in fact impossible for it to do so), all guest commands most be | |||
implemented as asynchronous commands within QEMU. This does not change anything | |||
from a protocol visible perspective but is simply an implementation detail | |||
within QEMU. | |||
== Security Considerations == | |||
The following security issues need to be resolved in QMP: | |||
1. The JSON parser uses a recursive decent parser. Malicious input could potentially cause a stack overflow. Either implement a recursion depth counter, or | |||
swith the parser to only use tail recursion. | |||
1. The JSON parser may not handle premature EOI all that well. I think I've worked out most of these issues but more rigorious testing is needed. |
Revision as of 14:37, 8 March 2011
Summary
Implement support for QMP commands and events that terminate and originate respectively within the guest using an agent built as part of QEMU.
Detailed Summary
There are two proposed ways to implement support for a guest agent today in QEMU both with advantages and trade-offs.
One approach takes QEMU completely out of the picture by exposing a guest agent over a transport (like virtio-serial) and then exposing that transport over a CharDriverState to be terminated in the management layer (ala libvirt). This model keeps complexity out of QEMU but exposes management tools directly to the guest potentially significantly increases the guest's attack surface. In addition, it makes live migration very challenging to coordinating while keeping communication transparent to the guest agent.
Another proposal terminates the guest agent directly within QEMU. This introduces considerable complexity into QEMU by creating a new RPC transport potentially increasing the guest attack surface. While it does improve support for live migration, it requires a parallel API within QMP to support management tools.
A potential middle ground is to adopt QMP as the RPC interface between the guest agent and QEMU and expose the guest commands as a QMP namespace. This eliminates the need for a parallel API, addresses live migration robustly, and by reusing the existing QMP infrastructure, avoids significantly increasing the guest attack surface.
Schema Definition
All guest commands will use a guest- prefix to distinguish the fact that the commands are handled by the guest. Likewise, events will carry a GUEST_ prefix. Type names (complex types and enums) do not require a special prefix. The following is an example of the proposed guest agent schema:
// qemu/qmp-schema.json [ 'guest-ping', {}, 'none' ] [ 'guest-view-file', {'filename', 'str'}, 'str' ] { 'GuestType': [ 'linux', 'windows', 'freebsd', 'netbsd', 'openbsd', 'solaris' ] } { 'GuestInfo': { 'os-type': 'GuestType', 'os-version': 'str', 'agent-version': 'str' } } { 'GUEST_STARTUP': { 'GuestInfo': 'str' } }
libqmp
In libqmp, the code generated for a guest command is identical to the code generated for a normal command.
For instance, the guest-view-file command will have the following signature:
char *qmp_guest_view_file(QmpSession *sess, const char *filename, Error **errp);
QEMU
The only role QEMU plays in guest commands is unmarshalling and remarshalling the input and output. This means that data from the guest is not being sent directly to a management tool which significantly decreases the guest attack surface.
This works by combining the existing unmarshalling code for the QMP server with the marshalling code from libqmp. Here is an example of the code that will be generated:
void qmp_guest_ping(Error **errp) { QDict *args = qdict_new(); QObject *ret_data;
// no arguments to marshal ret_data = qmp_guest_command_dispatch("guest-ping", args, errp); // ret_data should always be empty qobject_decref(ret_data); QDECREF(args); } static void qmp_marshal_guest_ping(const QDict *args, QObject **ret_data, Error **errp)
{
// no arguments to unmarshal qmp_guest_ping(errp); // no retval to marshal
}
virtio-serial Transport
The qmp_guest_command_dispatch command will take the QObjects and generate a QMP command to send to the guest. This will be sent to the guest via a new CharDriverState implementation. This CharDriverState will provide a backend to virtio-serial. It will essentially act as an in-memory chardev except that it will parse the input from the guest for invalid UTF-8 characters. If an invalid character is detected, the CharDriverState will generate a reset.
This behavior will be utilized by the guest in order to reset the QMP session after the guest agent restarts. The first byte it writes to virtio-serial will always be 0xFF.
Guest Agent
The guest agent will be a daemon that connects to a virtio-serial device and feeds the input to a JSON parser. When a new command is received, it will hand the command over to the QAPI generated dispatch routines.
The guest agent will implement the server side of the QMP commands using the native signature for the function.
Asynchronous Commands
Since QEMU cannot rely on the guest agent responding immediately to a command (it is in fact impossible for it to do so), all guest commands most be implemented as asynchronous commands within QEMU. This does not change anything from a protocol visible perspective but is simply an implementation detail within QEMU.
Security Considerations
The following security issues need to be resolved in QMP:
1. The JSON parser uses a recursive decent parser. Malicious input could potentially cause a stack overflow. Either implement a recursion depth counter, or swith the parser to only use tail recursion. 1. The JSON parser may not handle premature EOI all that well. I think I've worked out most of these issues but more rigorious testing is needed.