Generic support for TLS protocol & I/O channels
Subsystems (4)
cover Generic support for TLS protocol & I/O channels
The following series is a work-in-progress of my effort to bring TLS encryption support to all the QEMU socket based backends. The current patches just illustrate changes to the chardev backends, but future patches will cover NBD client/server and the migration client/server code. The original discussion was here:
https://lists.gnu.org/archive/html/qemu-devel/2015-02/msg00529.html
The bulk of the interesting work here has focused on pulling our the TLS code from the VNC server and turning it into a self-contained, reusable API. This has been accompanied with a general consolidation of all cryptographic related code. So we have one place in the code base for dealing with cryptographic hash functions (md5, sha, etc), ciphers (DES, AES, etc), and protocols (TLS). The benefit of this is that the rest of the code base doesn't have to be littered with #ifdef HAVE_GNUTLS conditionals - all the integration code for gnutls is in one place.
Building on that, the next step has been to define a generic I/O channels API (inspired by GIOChannel, but improved to better suit QEMU's requirements). This provides a higher level API for dealing with POSIX sockets, and running protocols such as TLS, SASL, and WebSockets over them. This again allowed the VNC code to be further untangled, so it doesn't have to directly know about TLS or WebSockets for the most part. This will allow us to add WebSockets support to the chardev backends too at some point.
The patches series is sequenced into a number of logical groupings, with a view to allowing the patches to be incrementally merged, rather than having to take the entire series in one go.
- QOM - a handful of minor enhancements & fixes to QOM, in particular some work to make handling of enum properties clearer, and to make instantiation of objects with properties simpler.
- Crypto - introduce crypto/ and include/crypto/ directories that contain APIs for hash functions, cipher functions and the TLS credential (x509 cert) handling and TLS protocol itself. This will be further enhanced down the line as & when I get time to integrate support for LUKS.
- Hash conversion - the quorum blockdrv and VNC server are converted to use the new crypto hash functions. This removes some of the #ifdef CONFIG_* conditionals from their codepaths
- Cipher conversion - the qcow(2) blockdrv and VNC server are converted to use the new crypto cipher functions. This removes their direct dependency on QEMU's in-tree AES and DES impls. If QEMU is linked to GNUTLS, this will now transparently use either libgcrypt or nettle for the AES & DES algorithms. These impls are more actively maintained than QEMU's built-in impls and also benefit from FIPS certification in some distros. The built-in impls of course remain for those building without GNUTLS.
- I/O Channels - introduce io/ and include/io/ directories that contain APIs for dealing with arbitrary bidirectional I/O channels. These can be POSIX sockets, fifos, pipes, or higher level layers such as TLS or WebSockets. Having a common API for all these concepts greatly facilitates the integration of TLS/WebSockets into the various areas of code in QEMU that are traditionally hardcoded to directly use the POSIX sockets API.
- VNC conversion - convert the VNC server to use the new I/O channels APIs. This enables nearly all the remaining #ifdef CONFIG_* conditionals to be removed from the VNC codebase, greatly clarifying its code.
- Chardev conversion - convert the chardev backend to use the new I/O channels APIs instead of GLib's GIOChannel. This also includes support for enabling TLS on the TCP chardev backend, nicely illustrating how the I/O channels APIs simplify support for such protocols.
I've attempted to get fairly complete API documentation coverage for all the new code files I've created here, with example code usage too. In addition, the crypto modules gain a number of new test suites to validate correctness of the implementations, since this is critical code to get right.
What I see still to be done (high == merge block, medium == nice to have for merge, low == do it later):
- The code is not fully ported to the Windows platform yet. In particular the QIOChannelSocket and QIOChannelFile classes are almost certainly broken & if they compile it is just luck. High priority.
- Need to validate the reference counting / lifecycle of the QIOChannel objects in chardev & vnc servers to ensure I've not introduced any race conditions in their usage vs client disconnects. High priority.
- Unit test coverage of all the QIOChannel subclasses to validate their correct operation. Medium priority
- A QIOChannelMemory implementation that provides a memory buffer backed I/O channel. Mostly this will be used for the unit test suite, but might find other uses at some point. Medium priority.
- APIs for establishing socket connections. Currently the QIOChannelSocket class is instantiated from a pre-connected socket file descriptor. It is desirable to have a constructor that just accepts a hostname/service/family and then performs the name resolution & connection code. This will make the API more consistent to use. Medium priority.
- A QIOChannelSASL implementation that provides integration for the SASL authentication protocol. This will allow the last custom I/O layer to be removed from the VNC server code. Low priority, since we don't immediately want/need SASL support in chardev/migraton/nbd code.
- A QIOChannelTelnet implementation that runs the telnet protocol, to replace the hacky telnet support that is hardwired into the chardev backends. Low priority.
- The crypto API could usefully gain a cipher backend that uses the Linux kernel crypto API as an alternative to nettle or libgcrypt. Low priority, nice to have.
While I will probably start work on it, I'm not intending to submit the update of the NBD/migration code, until this series has been positively reviewed and looks like it is close to accepted for merge, as there are already enough patches in this series as it is :-) The aim though is to convert the NBD code to use QIOChannel instead of direct sockets usage & add the TLS protocol extension previously discussed with the NBD spec maintainer. The migration code will either be adapted to use QIOChannel, or the QEMUFile code will be adapted to use QIOChannel. Undecided which is the best approach there at this time. Probably depends whether we can do a QIOChannelRDMA impl that has performance on parity with what exists today.
For those interesting in testing I have made the series available on github too
https://github.com/berrange/qemu/tree/qemu-io-channel-7
The diffstat may look alarming but a good portion is in the test suite and there's some quite verbose comments inline too which bulk it up:
Daniel P. Berrange (34):
ui: remove check for failure of qemu_acl_init()
qom: document user creatable object types in help text
qom: create objects in two phases
qom: add object_new_propv / object_new_proplist constructors
qom: make enum string tables const-correct
qom: add a object_property_add_enum helper method
qom: don't pass string table to object_get_enum method
crypto: introduce new module for computing hash digests
crypto: move built-in AES implementation into crypto/
crypto: move built-in D3DES implementation into crypto/
crypto: introduce generic cipher API & built-in implementation
crypto: add a gcrypt cipher implementation
crypto: add a nettle cipher implementation
crypto: introduce new module for handling TLS credentials
crypto: add sanity checking of TLS credentials
crypto: introduce new module for handling TLS sessions
block: convert quorum blockdrv to use crypto APIs
ui: convert VNC websockets to use crypto APIs
block: convert qcow/qcow2 to use generic cipher API
ui: convert VNC to use generic cipher API
io: add abstract QIOChannel classes
io: add helper module for creating watches on UNIX FDs
io: add QIOChannelSocket class
io: add QIOChannelFile class
io: add QIOTask class for async operations
io: add QIOChannelTLS class
io: pull Buffer code out of VNC module
io: add QIOChannelWebsock class
ui: convert VNC server to use QEMUIOChannelSocket classes
ui: convert VNC server to use QIOChannelTLS
ui: convert VNC server to use QIOChannelWebsock
char: convert from GIOChannel to QIOChannel
char: don't assume telnet initialization will not block
char: introduce support for TLS encrypted TCP chardev backend
Makefile.objs | 1 +
backends/hostmem.c | 22 +-
block/Makefile.objs | 2 +-
block/qcow.c | 100 ++-
block/qcow2-cluster.c | 46 +-
block/qcow2.c | 95 +--
block/qcow2.h | 13 +-
block/quorum.c | 38 +-
configure | 213 ++++---
crypto/Makefile.objs | 7 +
{util => crypto}/aes.c | 2 +-
crypto/cipher-builtin.c | 391 ++++++++++++
crypto/cipher-gcrypt.c | 204 ++++++
crypto/cipher-nettle.c | 226 +++++++
crypto/cipher.c | 31 +
ui/d3des.c => crypto/desrfb.c | 2 +-
crypto/hash.c | 202 ++++++
crypto/init.c | 160 +++++
crypto/tlscreds.c | 1093 ++++++++++++++++++++++++++++++++
crypto/tlssession.c | 546 ++++++++++++++++
include/{qemu => crypto}/aes.h | 0
include/crypto/cipher.h | 205 ++++++
ui/d3des.h => include/crypto/desrfb.h | 0
include/crypto/hash.h | 189 ++++++
include/crypto/init.h | 29 +
include/crypto/tlscreds.h | 135 ++++
include/crypto/tlssession.h | 313 ++++++++++
include/hw/qdev-core.h | 2 +-
include/io/buffer.h | 118 ++++
include/io/channel-file.h | 67 ++
include/io/channel-socket.h | 168 +++++
include/io/channel-tls.h | 142 +++++
include/io/channel-unix.h | 50 ++
include/io/channel-websock.h | 108 ++++
include/io/channel.h | 388 ++++++++++++
include/io/task.h | 168 +++++
include/qapi/util.h | 2 +-
include/qapi/visitor-impl.h | 6 +-
include/qapi/visitor.h | 2 +-
include/qom/object.h | 78 ++-
io/Makefile.objs | 8 +
io/buffer.c | 65 ++
io/channel-file.c | 198 ++++++
io/channel-socket.c | 572 +++++++++++++++++
io/channel-tls.c | 393 ++++++++++++
io/channel-unix.c | 100 +++
io/channel-websock.c | 976 +++++++++++++++++++++++++++++
io/channel.c | 178 ++++++
io/task.c | 84 +++
numa.c | 1 -
qapi-schema.json | 2 +
qapi/qapi-dealloc-visitor.c | 3 +-
qapi/qapi-util.c | 2 +-
qapi/qapi-visit-core.c | 6 +-
qemu-char.c | 798 ++++++++++++------------
qemu-options.hx | 137 +++-
qom/object.c | 141 ++++-
scripts/qapi-types.py | 4 +-
target-arm/crypto_helper.c | 2 +-
target-i386/fpu_helper.c | 1 -
target-i386/ops_sse.h | 2 +-
target-ppc/int_helper.c | 2 +-
tests/.gitignore | 9 +
tests/Makefile | 16 +-
tests/crypto-tls-helpers.c | 485 +++++++++++++++
tests/crypto-tls-helpers.h | 133 ++++
tests/pkix_asn1_tab.c | 1103 +++++++++++++++++++++++++++++++++
tests/test-crypto-cipher.c | 290 +++++++++
tests/test-crypto-hash.c | 209 +++++++
tests/test-crypto-tlscreds.c | 727 ++++++++++++++++++++++
tests/test-crypto-tlssession.c | 540 ++++++++++++++++
ui/Makefile.objs | 6 +-
ui/vnc-auth-sasl.c | 81 +--
ui/vnc-auth-vencrypt.c | 90 ++-
ui/vnc-enc-tight.c | 38 +-
ui/vnc-enc-zlib.c | 6 +-
ui/vnc-enc-zrle.c | 18 +-
ui/vnc-jobs.c | 25 +-
ui/vnc-tls.c | 24 +-
ui/vnc-tls.h | 69 ---
ui/vnc-ws.c | 393 ++----------
ui/vnc-ws.h | 75 +--
ui/vnc.c | 997 ++++++++++++++---------------
ui/vnc.h | 104 ++--
util/Makefile.objs | 2 +-
vl.c | 37 +-
86 files changed, 12868 insertions(+), 1848 deletions(-)
create mode 100644 crypto/Makefile.objs
rename {util => crypto}/aes.c (99%)
create mode 100644 crypto/cipher-builtin.c
create mode 100644 crypto/cipher-gcrypt.c
create mode 100644 crypto/cipher-nettle.c
create mode 100644 crypto/cipher.c
rename ui/d3des.c => crypto/desrfb.c (99%)
create mode 100644 crypto/hash.c
create mode 100644 crypto/init.c
create mode 100644 crypto/tlscreds.c
create mode 100644 crypto/tlssession.c
rename include/{qemu => crypto}/aes.h (100%)
create mode 100644 include/crypto/cipher.h
rename ui/d3des.h => include/crypto/desrfb.h (100%)
create mode 100644 include/crypto/hash.h
create mode 100644 include/crypto/init.h
create mode 100644 include/crypto/tlscreds.h
create mode 100644 include/crypto/tlssession.h
create mode 100644 include/io/buffer.h
create mode 100644 include/io/channel-file.h
create mode 100644 include/io/channel-socket.h
create mode 100644 include/io/channel-tls.h
create mode 100644 include/io/channel-unix.h
create mode 100644 include/io/channel-websock.h
create mode 100644 include/io/channel.h
create mode 100644 include/io/task.h
create mode 100644 io/Makefile.objs
create mode 100644 io/buffer.c
create mode 100644 io/channel-file.c
create mode 100644 io/channel-socket.c
create mode 100644 io/channel-tls.c
create mode 100644 io/channel-unix.c
create mode 100644 io/channel-websock.c
create mode 100644 io/channel.c
create mode 100644 io/task.c
create mode 100644 tests/crypto-tls-helpers.c
create mode 100644 tests/crypto-tls-helpers.h
create mode 100644 tests/pkix_asn1_tab.c
create mode 100644 tests/test-crypto-cipher.c
create mode 100644 tests/test-crypto-hash.c
create mode 100644 tests/test-crypto-tlscreds.c
create mode 100644 tests/test-crypto-tlssession.c
delete mode 100644 ui/vnc-tls.hSignature · 1 line
2.1.0
RFC PATCH 1/34 ui: remove check for failure of qemu_acl_init()
The qemu_acl_init() function has long since stopped being able to return NULL, since g_malloc will abort on OOM. As such the checks for NULL were unreachable code.
--- ui/vnc.c | 8 -------- 1 file changed, 8 deletions(-)
ui/vnc.c +0 -8
@@ -3602,10 +3602,6 @@ void vnc_display_open(const char *id, Error **errp) aclname = g_strdup_printf("vnc.%s.x509dname", vs->id); } vs->tls.acl =qemu_acl_init (aclname); -if (!vs->tls.acl) { -fprintf (stderr,"Failed to create x509 dname ACL\n" ); -exit (1 ); - }g_free (aclname); }#endif
@@ -3619,10 +3615,6 @@ void vnc_display_open(const char *id, Error **errp) aclname = g_strdup_printf("vnc.%s.username", vs->id); } vs->sasl.acl =qemu_acl_init (aclname); -if (!vs->sasl.acl) { -fprintf (stderr,"Failed to create username ACL\n" ); -exit (1 ); - }g_free (aclname); }#endif
Signature · 1 line
2.1.0
Re: [Qemu-devel] [PATCH v1 RFC 01/34] ui: remove check for failure of qemu_acl_init()
Quoted 8 lines · Daniel P. Berrange
The
qemu_acl_init()function has long since stopped being able to return NULL, sinceg_mallocwill abort on OOM. As such the checks for NULL were unreachable code.Signed-off-by Daniel P. Berrange <[email protected]>--- ui/vnc.c | 8 -------- 1 file changed, 8 deletions(-)
Signature · 2 lines
Eric Blake eblake redhat com +1-919-301-3266 Libvirt virtualization library http://libvirt.org
RFC PATCH 2/34 qom: document user creatable object types in help text
In reply to Daniel P. BerrangeThe QEMU help for -object is essentially useless, just giving users the generic syntax. Move it down into its own section and introduce a nested table where each user creatable object can be documented. The existing memory-backend-file, rng-random and rng-egd object types are documented.
--- qemu-options.hx | 70 ++++++++++++++++++++++++++++++++++++++++++++------------- 1 file changed, 54 insertions(+), 16 deletions(-)
qemu-options.hx +54 -16
@@ -3421,22 +3421,6 @@ DEF("no-kvm-irqchip", 0, QEMU_OPTION_no_kvm_irqchip, "", QEMU_ARCH_I386) HXCOMM Deprecated (ignored) DEF("tdf", 0, QEMU_OPTION_tdf,"", QEMU_ARCH_ALL) -DEF("object", HAS_ARG, QEMU_OPTION_object, - "-object TYPENAME[,PROP1=VALUE1,...]\n" - " create an new object of type TYPENAME setting properties\n" - " in the order they are specified. Note that the 'id'\n" - " property must be set. These objects are placed in the\n" - " '/objects' path.\n", - QEMU_ARCH_ALL) -STEXI -@item -object @var{typename}[,@var{prop1}=@var{value1},...] -@findex -object -Create an new object of type @var{typename} setting properties -in the order they are specified. Note that the 'id' -property must be set. These objects are placed in the -'/objects' path. -ETEXI - DEF("msg", HAS_ARG, QEMU_OPTION_msg, "-msg timestamp[=on|off]\n" " change the format of messages\n"
@@ -3462,6 +3446,60 @@ Dump json-encoded vmstate information for current machine type to file in @var{file} ETEXI +DEFHEADING(Generic object creation) + +DEF("object", HAS_ARG, QEMU_OPTION_object, + "-object TYPENAME[,PROP1=VALUE1,...]\n" + " create an new object of type TYPENAME setting properties\n" + " in the order they are specified. Note that the 'id'\n" + " property must be set. These objects are placed in the\n" + " '/objects' path.\n", + QEMU_ARCH_ALL) +STEXI +@item -object @var{typename}[,@var{prop1}=@var{value1},...] +@findex -object +Create an new object of type @var{typename} setting properties +in the order they are specified. Note that the 'id' +property must be set. These objects are placed in the +'/objects' path. + +@table @option + +@item -object memory-backend-file,id=@var{id},size=@var{size},mem-path=@var{dir},share=@var{on|off} + +Creates a memory file backend object, which can be used to back +the guest RAM with huge pages. The @option{id} parameter is a +unique ID that will be used to reference this memory region +when configuring the @option{-numa} argument. The @option{size} +option provides the size of the memory region, and accepts +common suffixes, eg @option{500M}. The @option{mem-path} provides +the path to either a shared memory or huge page filesystem mount. +The @option{share} boolean option determines whether the memory +region is marked as private to QEMU, or shared. The latter allows +a co-operating external process to access the QEMU memory region. + +@item -object rng-random,id=@var{id},filename=@var{/dev/random} + +Creates a random number generator backend which obtains entropy from +a device on the host. The @option{id} parameter is a unique ID that +will be used to reference this entropy backend from the @option{virtio-rng} +device. The @option{filename} parameter specifies which file to obtain +entropy from and if omitted defaults to @option{/dev/random}. + +@item -object rng-egd,id=@var{id},chardev=@var{chardevid} + +Creates a random number generator backend which obtains entropy from +an external daemon running on the host. The @option{id} parameter is +a unique ID that will be used to reference this entropy backend from +the @option{virtio-rng} device. The @option{chardev} parameter is +the unique ID of a character device backend that provides the connection +to the RNG daemon. + +@end table + +ETEXI + + HXCOMM This is the last statement. Insert new options before this line! STEXI @end table
Signature · 1 line
2.1.0
RFC PATCH 3/34 qom: create objects in two phases
In reply to Daniel P. BerrangeSome types of object must be created before chardevs, other types of object must be created after chardevs. As such there is no option but to create objects in two phases.
This takes the decision to create as many object types as possible in the first phase, and only delay those which have a dependency on the chardevs. Hopefully the set which need delaying will remain small.
--- vl.c | 29 ++++++++++++++++++++++++++++- 1 file changed, 28 insertions(+), 1 deletion(-)
vl.c +28 -1
@@ -2591,6 +2591,22 @@ static int machine_set_property(const char *name, const char *value,return 0 ; } + +static bool object_create_phase1 (const char *type) +{ +/* rng-egd requires that -chardev is processed first, +* so we must delay it until phase2 */ +if (g_str_equal (type,"rng-egd" )) { +return false; + } +return true; +} + +static bool object_create_phase2 (const char *type) +{ +return !object_create_phase1 (type); +} +static int object_create (QemuOpts *opts,void *opaque) {Error *err =NULL ;
@@ -2599,6 +2615,7 @@ static int object_create(QemuOpts *opts, void *opaque)void *dummy =NULL ;OptsVisitor *ov;QDict *pdict; +bool (*type_predicate)(const char *) = opaque; ov =opts_visitor_new (opts); pdict =qemu_opts_to_qdict (opts,NULL );
@@ -2613,6 +2630,9 @@ static int object_create(QemuOpts *opts, void *opaque)if (err) { goto out; } +if (!type_predicate (type)) { + goto out; + }qdict_del (pdict,"id" );visit_type_str (opts_get_visitor (ov), &id,"id" , &err);
@@ -4008,6 +4028,12 @@ int main(int argc, char **argv, char **envp)socket_init (); +if (qemu_opts_foreach (qemu_find_opts ("object" ), + object_create, + object_create_phase1,0 ) !=0 ) { +exit (1 ); + } +if (qemu_opts_foreach (qemu_find_opts ("chardev" ), chardev_init_func,NULL ,1 ) !=0 )exit (1 );#ifdef CONFIG_VIRTFS
@@ -4027,7 +4053,8 @@ int main(int argc, char **argv, char **envp) }if (qemu_opts_foreach (qemu_find_opts ("object" ), - object_create,NULL ,0 ) !=0 ) { + object_create, + object_create_phase2,0 ) !=0 ) {exit (1 ); }
Signature · 1 line
2.1.0
RFC PATCH 4/34 qom: add object_new_propv / object_new_proplist constructors
In reply to Daniel P. BerrangeIt is reasonably common to want to create an object, set a number of properties, register it in the hierarchy and then mark it as complete (if a user creatable type). This requires quite alot of error prone, verbose, boilerplate code to achieve.
The object_new_propv / object_new_proplist constructors will simplify this task by performing all required steps in one go, accepting the property names/values as variadic args.
Usage would be:
Error *err = NULL;
Object *obj;
obj = object_new_propv(TYPE_MEMORY_BACKEND_FILE,
"hostmem0",
&err,
"share", "yes",
"mem-path", "/dev/shm/somefile",
"prealloc", "yes",
"size": "1048576",
NULL);Note all property values are passed in string form and will be parsed into their required data types.
--- include/qom/object.h | 58 +++++++++++++++++++++++++++++++++++++++++++++++ qom/object.c | 64 ++++++++++++++++++++++++++++++++++++++++++++++++++++ tests/Makefile | 2 +- 3 files changed, 123 insertions(+), 1 deletion(-)
include/qom/object.h +58 -0
@@ -607,6 +607,64 @@ Object *object_new(const char *typename);Object *object_new_with_type (Type type);/** +* object_new_propv: +* @typename: The name of the type of the object to instantiate. +* @id: The unique ID of the object +* @errp: pointer to error object +* @...: list of property names and values +* +* This function with initialize a new object using heap allocated memory. +* The returned object has a reference count of 1, and will be freed when +* the last reference is dropped. +* +* The @id parameter will be used to register the object in the /objects +* hierarchy. +* +* The variadic parameters are a list of pairs of (propname, propvalue) +* strings. The propname of NULL indicates the end of the property +* list. If the object implements the user creatable interface, the +* object will be marked complete once all the properties have been +* processed. +* +* Error *err = NULL; +* Object *obj; +* +* obj = object_new_propv(TYPE_MEMORY_BACKEND_FILE, +* "hostmem0", +* &err, +* "share", "yes", +* "mem-path", "/dev/shm/somefile", +* "prealloc", "yes", +* "size": "1048576", +* NULL); +* +* if (!obj) { +* g_printerr("Cannot create memory backend: %s\n", +* error_get_pretty(err)); +* } +* +* Returns: The newly allocated, instantiated & initialized object. +*/ +Object *object_new_propv (const char *typename, +const char *id, +Error **errp, + ...); + +/** +* object_new_proplist: +* @typename: The name of the type of the object to instantiate. +* @id: The unique ID of the object +* @errp: pointer to error object +* @vargs: list of property names and values +* +* See object_new_propv for documentation. +*/ +Object *object_new_proplist (const char *typename, +const char *id, +Error **errp, +va_list vargs); + +/** * object_initialize_with_type: * @data: A pointer to the memory to be used for the object. * @size: The maximum size available at @data for the object.
qom/object.c +64 -0
76 lines not on this page · raw message
tests/Makefile +1 -1
@@ -240,7 +240,7 @@ test-qapi-obj-y = tests/test-qapi-visit.o tests/test-qapi-types.o \ $(test-obj-y): QEMU_INCLUDES += -Itests QEMU_CFLAGS += -I$(SRC_PATH)/tests -qom-core-obj = qom/object.o qom/qom-qobject.o qom/container.o +qom-core-obj = qom/object.o qom/qom-qobject.o qom/container.o qom/object_interfaces.o tests/check-qint$(EXESUF): tests/check-qint.o libqemuutil.a tests/check-qstring$(EXESUF): tests/check-qstring.o libqemuutil.a
Signature · 1 line
2.1.0
Re: [Qemu-devel] [PATCH v1 RFC 04/34] qom: add object_new_propv / object_new_proplist constructors
Quoted 19 lines · Daniel P. Berrange
qom/object.c +19
-0+ +Object *object_new_proplist (const char *typename, +const char *id, +Error **errp, +va_list vargs) +{ +Object *obj; +const char *propname; + + obj =object_new (typename); + +if (object_class_is_abstract (object_get_class (obj))) { +error_setg (errp,"object type '%s' is abstract" , typename); + goto error; + } + + propname = va_arg(vargs, char *); +while (propname !=NULL ) { + const char *value = va_arg(vargs, char *);
qom/object.c Object *object_new(const char *typename) · line 485
Quoted 10 lines
qom/object.c +10
-0+ +object_property_parse (obj, value, propname, errp); +if (*errp) { + goto error; + } + propname = va_arg(vargs, char *); + } + +object_property_add_child (container_get (object_get_root (),"/objects" ), + id, obj, errp);
I would pass the parent as an additional argument to object_new_proplist(). Otherwise looks good.
Paolo
Re: [Qemu-devel] [PATCH v1 RFC 04/34] qom: add object_new_propv / object_new_proplist constructors
Quoted 27 lines · Paolo Bonzini
Quoted 24 lines · Daniel P. Berrange
qom/object.c +24
-0+ +Object *object_new_proplist (const char *typename, +const char *id, +Error **errp, +va_list vargs) +{ +Object *obj; +const char *propname; + + obj =object_new (typename); + +if (object_class_is_abstract (object_get_class (obj))) { +error_setg (errp,"object type '%s' is abstract" , typename); + goto error; + } + + propname = va_arg(vargs, char *); +while (propname !=NULL ) { + const char *value = va_arg(vargs, char *); + +object_property_parse (obj, value, propname, errp); +if (*errp) { + goto error; + }
Quoted 8 lines
qom/object.c Object *object_new(const char *typename) · line 485
Quoted 5 lines
qom/object.c +5
-0+ propname = va_arg(vargs, char *); + } + +object_property_add_child (container_get (object_get_root (),"/objects" ), + id, obj, errp);I would pass the parent as an additional argument to
object_new_proplist(). Otherwise looks good.
Yep, that does rather make sense for flexibility.
Regards,
Daniel
Signature · 4 lines
|: http://berrange.com -o- http://www.flickr.com/photos/dberrange/ :| |: http://libvirt.org -o- http://virt-manager.org :| |: http://autobuild.org -o- http://search.cpan.org/~danberr/ :| |: http://entangle-photo.org -o- http://live.gnome.org/gtk-vnc :|
Re: [Qemu-devel] [PATCH v1 RFC 04/34] qom: add object_new_propv / object_new_proplist constructors
In reply to Daniel P. BerrangeQuoted 4 lines · Daniel P. Berrange
It is reasonably common to want to create an object, set a number of properties, register it in the hierarchy and then mark it as complete (if a user creatable type). This requires quite alot of error prone, verbose, boilerplate code to achieve.
s/alot/a lot/
Quoted 7 lines
The
object_new_propv/object_new_proplistconstructors will simplify this task by performing all required steps in one go, accepting the property names/values as variadic args.Usage would be:
Quoted 9 lines
Error *err = NULL;
Object *obj;
obj = object_new_propv(TYPE_MEMORY_BACKEND_FILE,
"hostmem0",
&err,
"share", "yes",
"mem-path", "/dev/shm/somefile",
"prealloc", "yes",
"size": "1048576",s/:/,/
Quoted 14 lines
NULL);Note all property values are passed in string form and will be parsed into their required data types.
Signed-off-by Daniel P. Berrange <[email protected]>--- include/qom/object.h | 58 +++++++++++++++++++++++++++++++++++++++++++++++ qom/object.c | 64 ++++++++++++++++++++++++++++++++++++++++++++++++++++ tests/Makefile | 2 +- 3 files changed, 123 insertions(+), 1 deletion(-)include/qom/object.h +0
-0
include/qom/object.h Object *object_new(const char *typename); · line 638
Quoted 10 lines
include/qom/object.h +8
-1--- a/include/qom/object.h +* +* obj = object_new_propv(TYPE_MEMORY_BACKEND_FILE, +* "hostmem0", +* &err, +* "share", "yes", +* "mem-path", "/dev/shm/somefile", +* "prealloc", "yes", +* "size": "1048576",
and again
include/qom/object.h Object *object_new(const char *typename); · line 651
Quoted 13 lines
+ * NULL);
+ *
+ * if (!obj) {
+ * g_printerr("Cannot create memory backend: %s\n",
+ *error_get_pretty(err));
+ * }
+ *
+ * Returns: The newly allocated, instantiated & initialized object.
+ */include/qom/object.h +4
-0+Object *object_new_propv (const char *typename, +const char *id, +Error **errp, + ...);
You probably want to use the gcc __attribute__((__sentinel__)), so that the compiler can ensure that the caller NULL-terminates their list.
Quoted 10 lines
include/qom/object.h +10
-0+Object *object_new_proplist (const char *typename, +const char *id, +Error **errp, +va_list vargs) +{ +Object *obj; +const char *propname; + + obj =object_new (typename); +
include/qom/object.h
Quoted 10 lines
include/qom/object.h +10
-0+if (object_class_is_abstract (object_get_class (obj))) { +error_setg (errp,"object type '%s' is abstract" , typename); + goto error; + } + + propname = va_arg(vargs, char *); +while (propname !=NULL ) { + const char *value = va_arg(vargs, char *); + +object_property_parse (obj, value, propname, errp);
Is it worth a sanity check of assert(value) prior to calling object_property_parse(), as I have the suspicion that it doesn't handle NULL very well?
Signature · 2 lines
Eric Blake eblake redhat com +1-919-301-3266 Libvirt virtualization library http://libvirt.org
RFC PATCH 5/34 qom: make enum string tables const-correct
In reply to Daniel P. BerrangeThe enum string table parameters in various QOM/QAPI methods are declared 'const char *strings[]'. This results in const warnings if passed a variable that was declared as
static const char * const strings[] = { .... };Add the extra const annotation to the parameters, since neither the string elements, nor the array itself should ever be modified.
--- include/hw/qdev-core.h | 2 +- include/qapi/util.h | 2 +- include/qapi/visitor-impl.h | 6 +++--- include/qapi/visitor.h | 2 +- include/qom/object.h | 2 +- qapi/qapi-dealloc-visitor.c | 3 ++- qapi/qapi-util.c | 2 +- qapi/qapi-visit-core.c | 6 +++--- qom/object.c | 2 +- scripts/qapi-types.py | 4 ++-- 10 files changed, 16 insertions(+), 15 deletions(-)
include/hw/qdev-core.h +1 -1
@@ -236,7 +236,7 @@ struct Property {struct PropertyInfo {const char *name;const char *description; -const char **enum_table; +const char *const *enum_table;int (*print)(DeviceState *dev,Property *prop,char *dest,size_t len);ObjectPropertyAccessor *get;ObjectPropertyAccessor *set;
include/qapi/util.h +1 -1
@@ -11,7 +11,7 @@#ifndef QAPI_UTIL_H #define QAPI_UTIL_H -int qapi_enum_parse (const char *lookup[],const char *buf, +int qapi_enum_parse (const char *const lookup[],const char *buf,int max,int def,Error **errp);#endif
include/qapi/visitor-impl.h +3 -3
@@ -30,7 +30,7 @@ struct Visitor GenericList *(*next_list)(Visitor *v, GenericList **list, Error **errp);void (*end_list)(Visitor *v,Error **errp); -void (*type_enum)(Visitor *v,int *obj,const char *strings[], +void (*type_enum)(Visitor *v,int *obj,const char *const strings[],const char *kind,const char *name,Error **errp);void (*get_next_type)(Visitor *v,int *kind,const int *qobjects,const char *name,Error **errp);
@@ -59,9 +59,9 @@ struct Visitor void (*end_union)(Visitor *v, bool data_present, Error **errp); }; -void input_type_enum (Visitor *v,int *obj,const char *strings[], +void input_type_enum (Visitor *v,int *obj,const char *const strings[],const char *kind,const char *name,Error **errp); -void output_type_enum (Visitor *v,int *obj,const char *strings[], +void output_type_enum (Visitor *v,int *obj,const char *const strings[],const char *kind,const char *name,Error **errp);#endif
include/qapi/visitor.h +1 -1
@@ -43,7 +43,7 @@ void visit_optional(Visitor *v, bool *present, const char *name, Error **errp);void visit_get_next_type (Visitor *v,int *obj,const int *qtypes,const char *name,Error **errp); -void visit_type_enum (Visitor *v,int *obj,const char *strings[], +void visit_type_enum (Visitor *v,int *obj,const char *const strings[],const char *kind,const char *name,Error **errp);void visit_type_int (Visitor *v,int64_t *obj,const char *name,Error **errp);void visit_type_uint8 (Visitor *v,uint8_t *obj,const char *name,Error **errp);
include/qom/object.h +1 -1
@@ -1011,7 +1011,7 @@ int64_t object_property_get_int(Object *obj, const char *name,* an enum). */ int object_property_get_enum (Object *obj,const char *name, -const char *strings[],Error **errp); +const char *const strings[],Error **errp);/** * object_property_get_uint16List:
qapi/qapi-dealloc-visitor.c +2 -1
@@ -156,7 +156,8 @@ static void qapi_dealloc_type_size(Visitor *v, uint64_t *obj, const char *name, { } -static void qapi_dealloc_type_enum (Visitor *v,int *obj,const char *strings[], +static void qapi_dealloc_type_enum (Visitor *v,int *obj, +const char *const strings[],const char *kind,const char *name,Error **errp) {
qapi/qapi-util.c +1 -1
@@ -14,7 +14,7 @@#include "qapi/error.h" #include "qapi/util.h" -int qapi_enum_parse (const char *lookup[],const char *buf, +int qapi_enum_parse (const char *const lookup[],const char *buf,int max,int def,Error **errp) {int i;
qapi/qapi-visit-core.c +3 -3
@@ -89,7 +89,7 @@ void visit_get_next_type(Visitor *v, int *obj, const int *qtypes, } } -void visit_type_enum (Visitor *v,int *obj,const char *strings[], +void visit_type_enum (Visitor *v,int *obj,const char *const strings[],const char *kind,const char *name,Error **errp) { v->type_enum (v, obj, strings, kind, name, errp);
@@ -260,7 +260,7 @@ void visit_type_number(Visitor *v, double *obj, const char *name, Error **errp) v->type_number (v, obj, name, errp); } -void output_type_enum (Visitor *v,int *obj,const char *strings[], +void output_type_enum (Visitor *v,int *obj,const char *const strings[],const char *kind,const char *name,Error **errp) {
@@ -279,7 +279,7 @@ void output_type_enum(Visitor *v, int *obj, const char *strings[],visit_type_str (v, &enum_str, name, errp); } -void input_type_enum (Visitor *v,int *obj,const char *strings[], +void input_type_enum (Visitor *v,int *obj,const char *const strings[],const char *kind,const char *name,Error **errp) {
qom/object.c +1 -1
@@ -1025,7 +1025,7 @@ int64_t object_property_get_int(Object *obj, const char *name, }int object_property_get_enum (Object *obj,const char *name, -const char *strings[],Error **errp) +const char *const strings[],Error **errp) {StringOutputVisitor *sov;StringInputVisitor *siv;
scripts/qapi-types.py +2 -2
@@ -118,7 +118,7 @@ struct %(name)s def generate_enum_lookup(name, values): ret = mcgen(''' -const char *%(name)s_lookup[] = { +const char * const %(name)s_lookup[] = { ''', name=name) i = 0
@@ -140,7 +140,7 @@ const char *%(name)s_lookup[] = { def generate_enum(name, values): lookup_decl = mcgen(''' -extern const char *%(name)s_lookup[]; +extern const char * const %(name)s_lookup[]; ''', name=name)
Signature · 1 line
2.1.0
Re: [Qemu-devel] [PATCH v1 RFC 05/34] qom: make enum string tables const-correct
Quoted 187 lines · Daniel P. Berrange
The enum string table parameters in various QOM/QAPI methods are declared 'const char *strings[]'. This results in const warnings if passed a variable that was declared as
static const char * const strings[] = { .... };Add the extra const annotation to the parameters, since neither the string elements, nor the array itself should ever be modified.
Signed-off-by Daniel P. Berrange <[email protected]>--- include/hw/qdev-core.h | 2 +- include/qapi/util.h | 2 +- include/qapi/visitor-impl.h | 6 +++--- include/qapi/visitor.h | 2 +- include/qom/object.h | 2 +- qapi/qapi-dealloc-visitor.c | 3 ++- qapi/qapi-util.c | 2 +- qapi/qapi-visit-core.c | 6 +++--- qom/object.c | 2 +- scripts/qapi-types.py | 4 ++-- 10 files changed, 16 insertions(+), 15 deletions(-)include/hw/qdev-core.h +1
-1@@ -236,7 +236,7 @@ struct Property {struct PropertyInfo {const char *name;const char *description; -const char **enum_table; +const char *const *enum_table;int (*print)(DeviceState *dev,Property *prop,char *dest,size_t len);ObjectPropertyAccessor *get;ObjectPropertyAccessor *set;include/qapi/util.h +1
-1@@ -11,7 +11,7 @@#ifndef QAPI_UTIL_H #define QAPI_UTIL_H -int qapi_enum_parse (const char *lookup[],const char *buf, +int qapi_enum_parse (const char *const lookup[],const char *buf,int max,int def,Error **errp);#endif include/qapi/visitor-impl.h +3
-3@@ -30,7 +30,7 @@ struct Visitor GenericList *(*next_list)(Visitor *v, GenericList **list, Error **errp);void (*end_list)(Visitor *v,Error **errp); -void (*type_enum)(Visitor *v,int *obj,const char *strings[], +void (*type_enum)(Visitor *v,int *obj,const char *const strings[],const char *kind,const char *name,Error **errp);void (*get_next_type)(Visitor *v,int *kind,const int *qobjects,const char *name,Error **errp);@@ -59,9 +59,9 @@ struct Visitor void (*end_union)(Visitor *v, bool data_present, Error **errp); }; -void input_type_enum (Visitor *v,int *obj,const char *strings[], +void input_type_enum (Visitor *v,int *obj,const char *const strings[],const char *kind,const char *name,Error **errp); -void output_type_enum (Visitor *v,int *obj,const char *strings[], +void output_type_enum (Visitor *v,int *obj,const char *const strings[],const char *kind,const char *name,Error **errp);#endif include/qapi/visitor.h +1
-1@@ -43,7 +43,7 @@ void visit_optional(Visitor *v, bool *present, const char *name, Error **errp);void visit_get_next_type (Visitor *v,int *obj,const int *qtypes,const char *name,Error **errp); -void visit_type_enum (Visitor *v,int *obj,const char *strings[], +void visit_type_enum (Visitor *v,int *obj,const char *const strings[],const char *kind,const char *name,Error **errp);void visit_type_int (Visitor *v,int64_t *obj,const char *name,Error **errp);void visit_type_uint8 (Visitor *v,uint8_t *obj,const char *name,Error **errp);include/qom/object.h +1
-1@@ -1011,7 +1011,7 @@ int64_t object_property_get_int(Object *obj, const char *name,* an enum). */ int object_property_get_enum (Object *obj,const char *name, -const char *strings[],Error **errp); +const char *const strings[],Error **errp);/** * object_property_get_uint16List: qapi/qapi-dealloc-visitor.c +2
-1@@ -156,7 +156,8 @@ static void qapi_dealloc_type_size(Visitor *v, uint64_t *obj, const char *name, { } -static void qapi_dealloc_type_enum (Visitor *v,int *obj,const char *strings[], +static void qapi_dealloc_type_enum (Visitor *v,int *obj, +const char *const strings[],const char *kind,const char *name,Error **errp) {qapi/qapi-util.c +1
-1@@ -14,7 +14,7 @@#include "qapi/error.h" #include "qapi/util.h" -int qapi_enum_parse (const char *lookup[],const char *buf, +int qapi_enum_parse (const char *const lookup[],const char *buf,int max,int def,Error **errp) {int i;qapi/qapi-visit-core.c +3
-3@@ -89,7 +89,7 @@ void visit_get_next_type(Visitor *v, int *obj, const int *qtypes, } } -void visit_type_enum (Visitor *v,int *obj,const char *strings[], +void visit_type_enum (Visitor *v,int *obj,const char *const strings[],const char *kind,const char *name,Error **errp) { v->type_enum (v, obj, strings, kind, name, errp);@@ -260,7 +260,7 @@ void visit_type_number(Visitor *v, double *obj, const char *name, Error **errp) v->type_number (v, obj, name, errp); } -void output_type_enum (Visitor *v,int *obj,const char *strings[], +void output_type_enum (Visitor *v,int *obj,const char *const strings[],const char *kind,const char *name,Error **errp) {@@ -279,7 +279,7 @@ void output_type_enum(Visitor *v, int *obj, const char *strings[],visit_type_str (v, &enum_str, name, errp); } -void input_type_enum (Visitor *v,int *obj,const char *strings[], +void input_type_enum (Visitor *v,int *obj,const char *const strings[],const char *kind,const char *name,Error **errp) {qom/object.c +1
-1@@ -1025,7 +1025,7 @@ int64_t object_property_get_int(Object *obj, const char *name, }int object_property_get_enum (Object *obj,const char *name, -const char *strings[],Error **errp) +const char *const strings[],Error **errp) {StringOutputVisitor *sov;StringInputVisitor *siv;scripts/qapi-types.py +1
-1@@ -118,7 +118,7 @@ struct %(name)s def generate_enum_lookup(name, values): ret = mcgen(''' -const char *%(name)s_lookup[] = { +const char * const %(name)s_lookup[] = { ''', name=name) i = 0
scripts/qapi-types.py const char *%(name)s_lookup[] = { · line 146
Quoted 8 lines
scripts/qapi-types.py +1
-1@@ -140,7 +140,7 @@ const char *%(name)s_lookup[] = { def generate_enum(name, values): lookup_decl = mcgen(''' -extern const char *%(name)s_lookup[]; +extern const char * const %(name)s_lookup[]; ''', name=name)
RFC PATCH 6/34 qom: add a object_property_add_enum helper method
In reply to Daniel P. BerrangeA QOM property can be parsed as enum using the visit_type_enum() helper method, but this forces callers to use the more complex generic object_property_add() method when registering it. It also requires that users of that object have access to the string map when they want to read the property value.
This patch introduces a specialized object_property_add_enum() method which simplifies the use of enum properties, so the setters/getters directly get passed the int value.
typedef enum {
MYDEV_TYPE_FROG,
MYDEV_TYPE_ALLIGATOR,
MYDEV_TYPE_PLATYPUS, MYDEV_TYPE_LAST
} MyDevType;
Then provide a table of enum <-> string mappings
static const char *const mydevtypemap[MYDEV_TYPE_LAST + 1] = {
[MYDEV_TYPE_FROG] = "frog",
[MYDEV_TYPE_ALLIGATOR] = "alligator",
[MYDEV_TYPE_PLATYPUS] = "platypus",
[MYDEV_TYPE_LAST] = NULL,
};Assuming an object struct of
typedef struct {
Object parent;
MyDevType devtype;
...other fields...
} MyDev;The property can then be registered as follows:
static int mydev_prop_get_devtype(Object *obj,
Error **errp G_GNUC_UNUSED)
{
MyDev *dev = MYDEV(obj);return dev->devtype;
}
static void mydev_prop_set_devtype(Object *obj,
int value,
Error **errp G_GNUC_UNUSED)
{
MyDev *dev = MYDEV(obj);dev->endpoint = value;
}
object_property_add_enum(obj, "devtype",
mydevtypemap,
mydev_prop_get_devtype,
mydev_prop_set_devtype,
NULL);Note there is no need to check the range of 'value' in the setter, because the string->enum conversion code will have already done that and reported an error as required.
--- include/qom/object.h | 17 ++++++++++++++++ qom/object.c | 57 ++++++++++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 74 insertions(+)
include/qom/object.h +17 -0
@@ -1262,6 +1262,23 @@ void object_property_add_bool(Object *obj, const char *name, Error **errp);/** +* object_property_add_enum: +* @obj: the object to add a property to +* @name: the name of the property +* @get: the getter or NULL if the property is write-only. +* @set: the setter or NULL if the property is read-only +* @errp: if an error occurs, a pointer to an area to store the error +* +* Add a enum property using getters/setters. This function will add a +* property of type 'enum'. +*/ +void object_property_add_enum (Object *obj,const char *name, +const char *const *strings, +int (*get)(Object *,Error **), +void (*set)(Object *,int ,Error **), +Error **errp); + +/** * object_property_add_tm: * @obj: the object to add a property to * @name: the name of the property
qom/object.c +57 -0
63 lines not on this page · raw message
Signature · 1 line
2.1.0
Re: [Qemu-devel] [PATCH v1 RFC 06/34] qom: add a object_property_add_enum helper method
Quoted 157 lines · Daniel P. Berrange
A QOM property can be parsed as enum using the
visit_type_enum()helper method, but this forces callers to use the more complex genericobject_property_add()method when registering it. It also requires that users of that object have access to the string map when they want to read the property value.This patch introduces a specialized
object_property_add_enum()method which simplifies the use of enum properties, so the setters/getters directly get passed the int value.typedef enum { MYDEV_TYPE_FROG, MYDEV_TYPE_ALLIGATOR, MYDEV_TYPE_PLATYPUS,
MYDEV_TYPE_LAST
} MyDevType;Then provide a table of enum <-> string mappings
static const char *const mydevtypemap[MYDEV_TYPE_LAST + 1] = { [MYDEV_TYPE_FROG] = "frog", [MYDEV_TYPE_ALLIGATOR] = "alligator", [MYDEV_TYPE_PLATYPUS] = "platypus", [MYDEV_TYPE_LAST] = NULL, };Assuming an object struct of
typedef struct { Object parent; MyDevType devtype; ...other fields... } MyDev;The property can then be registered as follows:
static int mydev_prop_get_devtype(Object *obj, Error **errp G_GNUC_UNUSED) { MyDev *dev = MYDEV(obj);return dev->devtype;}
static void mydev_prop_set_devtype(Object *obj, int value, Error **errp G_GNUC_UNUSED) { MyDev *dev = MYDEV(obj);dev->endpoint = value;}
object_property_add_enum(obj, "devtype", mydevtypemap, mydev_prop_get_devtype, mydev_prop_set_devtype, NULL);Note there is no need to check the range of 'value' in the setter, because the
string->enumconversion code will have already done that and reported an error as required.Signed-off-by Daniel P. Berrange <[email protected]>--- include/qom/object.h | 17 ++++++++++++++++ qom/object.c | 57 ++++++++++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 74 insertions(+)include/qom/object.h +17
-0@@ -1262,6 +1262,23 @@ void object_property_add_bool(Object *obj, const char *name, Error **errp);/** +* object_property_add_enum: +* @obj: the object to add a property to +* @name: the name of the property +* @get: the getter or NULL if the property is write-only. +* @set: the setter or NULL if the property is read-only +* @errp: if an error occurs, a pointer to an area to store the error +* +* Add a enum property using getters/setters. This function will add a +* property of type 'enum'. +*/ +void object_property_add_enum (Object *obj,const char *name, +const char *const *strings, +int (*get)(Object *,Error **), +void (*set)(Object *,int ,Error **), +Error **errp); + +/** * object_property_add_tm: * @obj: the object to add a property to * @name: the name of the property qom/object.c +50
-0@@ -1607,6 +1607,63 @@ void object_property_add_bool(Object *obj, const char *name, } } +typedef struct EnumProperty { +const char *const *strings; +int (*get)(Object *,Error **); +void (*set)(Object *,int ,Error **); +}EnumProperty ; + +static void property_get_enum (Object *obj,Visitor *v,void *opaque, +const char *name,Error **errp) +{ +EnumProperty *prop = opaque; +int value; + + value = prop->get (obj, errp); +visit_type_enum (v, &value, prop->strings,NULL , name, errp); +} + +static void property_set_enum (Object *obj,Visitor *v,void *opaque, +const char *name,Error **errp) +{ +EnumProperty *prop = opaque; +int value; + +visit_type_enum (v, &value, prop->strings,NULL , name, errp); + prop->set (obj, value, errp); +} + +static void property_release_enum (Object *obj,const char *name, +void *opaque) +{ +EnumProperty *prop = opaque; +g_free (prop); +} + +void object_property_add_enum (Object *obj,const char *name, +const char *const *strings, +int (*get)(Object *,Error **), +void (*set)(Object *,int ,Error **), +Error **errp) +{ +Error *local_err =NULL ; +EnumProperty *prop =g_malloc0 (sizeof (*prop)); + + prop->strings = strings; + prop->get = get; + prop->set = set; + + object_property_add(obj, name, "enum", + get ? property_get_enum : NULL, + set ? property_set_enum : NULL, + property_release_enum,
qom/object.c void object_property_add_bool(Object *obj, const char *name, · line 1669
Quoted 10 lines
qom/object.c +7
-0+ prop, &local_err); +if (local_err) { +error_propagate (errp, local_err); +g_free (prop); + } +} +typedef struct TMProperty {void (*get)(Object *,struct tm *,Error **); }TMProperty ;
Re: [Qemu-devel] [PATCH v1 RFC 06/34] qom: add a object_property_add_enum helper method
Quoted 54 lines · Paolo Bonzini
Quoted 51 lines · Daniel P. Berrange
A QOM property can be parsed as enum using the
visit_type_enum()helper method, but this forces callers to use the more complex genericobject_property_add()method when registering it. It also requires that users of that object have access to the string map when they want to read the property value.This patch introduces a specialized
object_property_add_enum()method which simplifies the use of enum properties, so the setters/getters directly get passed the int value.typedef enum { MYDEV_TYPE_FROG, MYDEV_TYPE_ALLIGATOR, MYDEV_TYPE_PLATYPUS,
MYDEV_TYPE_LAST
} MyDevType;Then provide a table of enum <-> string mappings
static const char *const mydevtypemap[MYDEV_TYPE_LAST + 1] = { [MYDEV_TYPE_FROG] = "frog", [MYDEV_TYPE_ALLIGATOR] = "alligator", [MYDEV_TYPE_PLATYPUS] = "platypus", [MYDEV_TYPE_LAST] = NULL, };Assuming an object struct of
typedef struct { Object parent; MyDevType devtype; ...other fields... } MyDev;The property can then be registered as follows:
static int mydev_prop_get_devtype(Object *obj, Error **errp G_GNUC_UNUSED) { MyDev *dev = MYDEV(obj);return dev->devtype;}
static void mydev_prop_set_devtype(Object *obj, int value, Error **errp G_GNUC_UNUSED) { MyDev *dev = MYDEV(obj);
Quoted 8 lines
Quoted 8 lines
dev->endpoint = value;}
object_property_add_enum(obj, "devtype", mydevtypemap, mydev_prop_get_devtype, mydev_prop_set_devtype, NULL);
On second thought (after seeing patch 7), please add a property type argument here. We lose introspection of enum property types otherwise.
It's possible to use macros so that 'MyEnum' gets translated to two arguments '"MyEnum", MyEnum_lookup', but I don't think that's worth the obfuscation.
Paolo
Quoted 111 lines
Quoted 97 lines
Note there is no need to check the range of 'value' in the setter, because the
string->enumconversion code will have already done that and reported an error as required.Signed-off-by Daniel P. Berrange <[email protected]>--- include/qom/object.h | 17 ++++++++++++++++ qom/object.c | 57 ++++++++++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 74 insertions(+)include/qom/object.h +17
-0@@ -1262,6 +1262,23 @@ void object_property_add_bool(Object *obj, const char *name, Error **errp);/** +* object_property_add_enum: +* @obj: the object to add a property to +* @name: the name of the property +* @get: the getter or NULL if the property is write-only. +* @set: the setter or NULL if the property is read-only +* @errp: if an error occurs, a pointer to an area to store the error +* +* Add a enum property using getters/setters. This function will add a +* property of type 'enum'. +*/ +void object_property_add_enum (Object *obj,const char *name, +const char *const *strings, +int (*get)(Object *,Error **), +void (*set)(Object *,int ,Error **), +Error **errp); + +/** * object_property_add_tm: * @obj: the object to add a property to * @name: the name of the property qom/object.c +50
-0@@ -1607,6 +1607,63 @@ void object_property_add_bool(Object *obj, const char *name, } } +typedef struct EnumProperty { +const char *const *strings; +int (*get)(Object *,Error **); +void (*set)(Object *,int ,Error **); +}EnumProperty ; + +static void property_get_enum (Object *obj,Visitor *v,void *opaque, +const char *name,Error **errp) +{ +EnumProperty *prop = opaque; +int value; + + value = prop->get (obj, errp); +visit_type_enum (v, &value, prop->strings,NULL , name, errp); +} + +static void property_set_enum (Object *obj,Visitor *v,void *opaque, +const char *name,Error **errp) +{ +EnumProperty *prop = opaque; +int value; + +visit_type_enum (v, &value, prop->strings,NULL , name, errp); + prop->set (obj, value, errp); +} + +static void property_release_enum (Object *obj,const char *name, +void *opaque) +{ +EnumProperty *prop = opaque; +g_free (prop); +} + +void object_property_add_enum (Object *obj,const char *name, +const char *const *strings, +int (*get)(Object *,Error **), +void (*set)(Object *,int ,Error **), +Error **errp) +{ +Error *local_err =NULL ; +EnumProperty *prop =g_malloc0 (sizeof (*prop)); + + prop->strings = strings; + prop->get = get; + prop->set = set; + + object_property_add(obj, name, "enum", + get ? property_get_enum : NULL, + set ? property_set_enum : NULL, + property_release_enum,qom/object.c void object_property_add_bool(Object *obj, const char *name, · line 1669
Quoted 10 lines
qom/object.c +7
-0+ prop, &local_err); +if (local_err) { +error_propagate (errp, local_err); +g_free (prop); + } +} +typedef struct TMProperty {void (*get)(Object *,struct tm *,Error **); }TMProperty ;Reviewed-by Paolo Bonzini <[email protected]>
Re: [Qemu-devel] [PATCH v1 RFC 06/34] qom: add a object_property_add_enum helper method
Quoted 60 lines · Paolo Bonzini
Quoted 57 lines · Paolo Bonzini
Quoted 54 lines · Daniel P. Berrange
A QOM property can be parsed as enum using the
visit_type_enum()helper method, but this forces callers to use the more complex genericobject_property_add()method when registering it. It also requires that users of that object have access to the string map when they want to read the property value.This patch introduces a specialized
object_property_add_enum()method which simplifies the use of enum properties, so the setters/getters directly get passed the int value.typedef enum { MYDEV_TYPE_FROG, MYDEV_TYPE_ALLIGATOR, MYDEV_TYPE_PLATYPUS,
MYDEV_TYPE_LAST
} MyDevType;Then provide a table of enum <-> string mappings
static const char *const mydevtypemap[MYDEV_TYPE_LAST + 1] = { [MYDEV_TYPE_FROG] = "frog", [MYDEV_TYPE_ALLIGATOR] = "alligator", [MYDEV_TYPE_PLATYPUS] = "platypus", [MYDEV_TYPE_LAST] = NULL, };Assuming an object struct of
typedef struct { Object parent; MyDevType devtype; ...other fields... } MyDev;The property can then be registered as follows:
static int mydev_prop_get_devtype(Object *obj, Error **errp G_GNUC_UNUSED) { MyDev *dev = MYDEV(obj);return dev->devtype;}
static void mydev_prop_set_devtype(Object *obj, int value, Error **errp G_GNUC_UNUSED) { MyDev *dev = MYDEV(obj);dev->endpoint = value;}
Quoted 8 lines
Quoted 5 lines
Quoted 5 lines
object_property_add_enum(obj, "devtype", mydevtypemap, mydev_prop_get_devtype, mydev_prop_set_devtype, NULL);On second thought (after seeing patch 7), please add a property type argument here. We lose introspection of enum property types otherwise.
Can you give me a pointer to where we lose introspection ? The hostmem code just registers its 'policy' property with a type name of 'str', which I've just changed to 'enum' in patch 7, so I didn't think I was regressing anything. Is there some QMP command which you think looses the info that I can verify.
Quoted 3 lines
It's possible to use macros so that 'MyEnum' gets translated to two arguments '"MyEnum",
MyEnum_lookup', but I don't think that's worth the obfuscation.
Regards,
Daniel
Signature · 4 lines
|: http://berrange.com -o- http://www.flickr.com/photos/dberrange/ :| |: http://libvirt.org -o- http://virt-manager.org :| |: http://autobuild.org -o- http://search.cpan.org/~danberr/ :| |: http://entangle-photo.org -o- http://live.gnome.org/gtk-vnc :|
Re: [Qemu-devel] [PATCH v1 RFC 06/34] qom: add a object_property_add_enum helper method
Quoted 5 lines · Daniel P. Berrange
Quoted 2 lines
On second thought (after seeing patch 7), please add a property type argument here. We lose introspection of enum property types otherwise.
Can you give me a pointer to where we lose introspection ? The hostmem code just registers its 'policy' property with a type name of 'str',
That was already a bug. :( It should have been registered as "HostMemPolicy". See for example:
$ qemu-system-x86_64 -device ide-hd,help ide-hd.bootindex=int32 ide-hd.unit=uint32 ide-hd.bios-chs-trans=BiosAtaTranslation (Logical CHS translation algorithm, auto/none/lba/large/rechs) ...
Quoted 3 lines
which I've just changed to 'enum' in patch 7, so I didn't think I was regressing anything. Is there some QMP command which you think looses the info that I can verify.
The qom-list command returns a list of property names and types.
Paolo
Re: [Qemu-devel] [PATCH v1 RFC 06/34] qom: add a object_property_add_enum helper method
Quoted 11 lines · Paolo Bonzini
Quoted 5 lines · Daniel P. Berrange
Quoted 2 lines
On second thought (after seeing patch 7), please add a property type argument here. We lose introspection of enum property types otherwise.
Can you give me a pointer to where we lose introspection ? The hostmem code just registers its 'policy' property with a type name of 'str',
That was already a bug. :( It should have been registered as "HostMemPolicy". See for example:
Ok, next time I'll probably send a patch which fixes that bug independantly and then does the conversion, in case we want the fix on stable too.
Quoted 11 lines
$ qemu-system-x86_64 -device ide-hd,help ide-hd.bootindex=int32 ide-hd.unit=uint32 ide-hd.bios-chs-trans=BiosAtaTranslation (Logical CHS translation algorithm, auto/none/lba/large/rechs) ...Quoted 3 lines
which I've just changed to 'enum' in patch 7, so I didn't think I was regressing anything. Is there some QMP command which you think looses the info that I can verify.
The qom-list command returns a list of property names and types.
Thanks, will look at that.
Regards,
Daniel
Signature · 4 lines
|: http://berrange.com -o- http://www.flickr.com/photos/dberrange/ :| |: http://libvirt.org -o- http://virt-manager.org :| |: http://autobuild.org -o- http://search.cpan.org/~danberr/ :| |: http://entangle-photo.org -o- http://live.gnome.org/gtk-vnc :|
RFC PATCH 7/34 qom: don't pass string table to object_get_enum method
In reply to Daniel P. BerrangeNow that properties can be explicitly registered as an enum type, there is no need to pass the string table to the object_get_enum method. The object property registration already has a pointer to the string table.
In changing this method signature, the hostmem backend object has to be converted to use the new enum property registration code, which simplifies it somewhat.
--- backends/hostmem.c | 22 ++++++++-------------- include/qom/object.h | 3 +-- numa.c | 1 - qom/object.c | 32 ++++++++++++++++++++++++-------- 4 files changed, 33 insertions(+), 25 deletions(-)
backends/hostmem.c +8 -14
@@ -113,24 +113,17 @@ host_memory_backend_set_host_nodes(Object *obj, Visitor *v, void *opaque,#endif } -static void -host_memory_backend_get_policy (Object *obj,Visitor *v,void *opaque, -const char *name,Error **errp) +static int +host_memory_backend_get_policy(Object *obj, Error **errp G_GNUC_UNUSED) {HostMemoryBackend *backend =MEMORY_BACKEND (obj); -int policy = backend->policy; - -visit_type_enum (v, &policy, HostMemPolicy_lookup,NULL , name, errp); +return backend->policy; }static void -host_memory_backend_set_policy (Object *obj,Visitor *v,void *opaque, -const char *name,Error **errp) +host_memory_backend_set_policy (Object *obj,int policy,Error **errp) {HostMemoryBackend *backend =MEMORY_BACKEND (obj); -int policy; - -visit_type_enum (v, &policy, HostMemPolicy_lookup,NULL , name, errp); backend->policy = policy;#ifndef CONFIG_NUMA
@@ -252,9 +245,10 @@ static void host_memory_backend_init(Object *obj)object_property_add (obj,"host-nodes" ,"int" , host_memory_backend_get_host_nodes, host_memory_backend_set_host_nodes,NULL ,NULL ,NULL ); -object_property_add (obj,"policy" ,"str" , - host_memory_backend_get_policy, - host_memory_backend_set_policy,NULL ,NULL ,NULL ); +object_property_add_enum (obj,"policy" , + HostMemPolicy_lookup, + host_memory_backend_get_policy, + host_memory_backend_set_policy,NULL ); } MemoryRegion *
include/qom/object.h +1 -2
@@ -1003,7 +1003,6 @@ int64_t object_property_get_int(Object *obj, const char *name,* object_property_get_enum: * @obj: the object * @name: the name of the property -* @strings: strings corresponding to enums * @errp: returns an error if this function fails * * Returns: the value of the property, converted to an integer, or
@@ -1011,7 +1010,7 @@ int64_t object_property_get_int(Object *obj, const char *name,* an enum). */ int object_property_get_enum (Object *obj,const char *name, -const char *const strings[],Error **errp); +Error **errp);/** * object_property_get_uint16List:
numa.c +0 -1
@@ -457,7 +457,6 @@ static int query_memdev(Object *obj, void *opaque) m->value->policy =object_property_get_enum (obj,"policy" , - HostMemPolicy_lookup, &err);if (err) { goto error;
qom/object.c +24 -8
@@ -1024,13 +1024,35 @@ int64_t object_property_get_int(Object *obj, const char *name,return retval; } +typedef struct EnumProperty { +const char *const *strings; +int (*get)(Object *,Error **); +void (*set)(Object *,int ,Error **); +}EnumProperty ; + +int object_property_get_enum (Object *obj,const char *name, -const char *const strings[],Error **errp) +Error **errp) {StringOutputVisitor *sov;StringInputVisitor *siv;char *str;int ret; +ObjectProperty *prop =object_property_find (obj, name, errp); +EnumProperty *enumprop; + +if (prop ==NULL ) { +return 0 ; + } + +if (!g_str_equal (prop->type,"enum" )) { +error_setg (errp,"Property %s on %s is not an 'enum' type" , + name,object_class_get_name ( +object_get_class (obj))); +return 0 ; + } + + enumprop = prop->opaque; sov =string_output_visitor_new (false);object_property_get (obj,string_output_get_visitor (sov), name, errp);
@@ -1038,7 +1060,7 @@ int object_property_get_enum(Object *obj, const char *name, siv =string_input_visitor_new (str);string_output_visitor_cleanup (sov);visit_type_enum (string_input_get_visitor (siv), - &ret, strings,NULL , name, errp); + &ret, enumprop->strings,NULL , name, errp);g_free (str);string_input_visitor_cleanup (siv);
@@ -1607,12 +1629,6 @@ void object_property_add_bool(Object *obj, const char *name, } } -typedef struct EnumProperty { -const char *const *strings; -int (*get)(Object *,Error **); -void (*set)(Object *,int ,Error **); -}EnumProperty ; -static void property_get_enum (Object *obj,Visitor *v,void *opaque,const char *name,Error **errp) {
Signature · 1 line
2.1.0
Re: [Qemu-devel] [PATCH v1 RFC 07/34] qom: don't pass string table to object_get_enum method
Quoted 118 lines · Daniel P. Berrange
Now that properties can be explicitly registered as an enum type, there is no need to pass the string table to the
object_get_enummethod. The object property registration already has a pointer to the string table.In changing this method signature, the hostmem backend object has to be converted to use the new enum property registration code, which simplifies it somewhat.
Signed-off-by Daniel P. Berrange <[email protected]>--- backends/hostmem.c | 22 ++++++++-------------- include/qom/object.h | 3 +-- numa.c | 1 - qom/object.c | 32 ++++++++++++++++++++++++-------- 4 files changed, 33 insertions(+), 25 deletions(-)backends/hostmem.c +8
-14@@ -113,24 +113,17 @@ host_memory_backend_set_host_nodes(Object *obj, Visitor *v, void *opaque,#endif } -static void -host_memory_backend_get_policy (Object *obj,Visitor *v,void *opaque, -const char *name,Error **errp) +static int +host_memory_backend_get_policy(Object *obj, Error **errp G_GNUC_UNUSED) {HostMemoryBackend *backend =MEMORY_BACKEND (obj); -int policy = backend->policy; - -visit_type_enum (v, &policy, HostMemPolicy_lookup,NULL , name, errp); +return backend->policy; }static void -host_memory_backend_set_policy (Object *obj,Visitor *v,void *opaque, -const char *name,Error **errp) +host_memory_backend_set_policy (Object *obj,int policy,Error **errp) {HostMemoryBackend *backend =MEMORY_BACKEND (obj); -int policy; - -visit_type_enum (v, &policy, HostMemPolicy_lookup,NULL , name, errp); backend->policy = policy;#ifndef CONFIG_NUMA @@ -252,9 +245,10 @@ static void host_memory_backend_init(Object *obj)object_property_add (obj,"host-nodes" ,"int" , host_memory_backend_get_host_nodes, host_memory_backend_set_host_nodes,NULL ,NULL ,NULL ); -object_property_add (obj,"policy" ,"str" , - host_memory_backend_get_policy, - host_memory_backend_set_policy,NULL ,NULL ,NULL ); +object_property_add_enum (obj,"policy" , + HostMemPolicy_lookup, + host_memory_backend_get_policy, + host_memory_backend_set_policy,NULL ); } MemoryRegion *include/qom/object.h +1
-2@@ -1003,7 +1003,6 @@ int64_t object_property_get_int(Object *obj, const char *name,* object_property_get_enum: * @obj: the object * @name: the name of the property -* @strings: strings corresponding to enums * @errp: returns an error if this function fails * * Returns: the value of the property, converted to an integer, or @@ -1011,7 +1010,7 @@ int64_t object_property_get_int(Object *obj, const char *name,* an enum). */ int object_property_get_enum (Object *obj,const char *name, -const char *const strings[],Error **errp); +Error **errp);/** * object_property_get_uint16List: numa.c +0
-1@@ -457,7 +457,6 @@ static int query_memdev(Object *obj, void *opaque) m->value->policy =object_property_get_enum (obj,"policy" , - HostMemPolicy_lookup, &err);if (err) { goto error;qom/object.c +8
-1@@ -1024,13 +1024,35 @@ int64_t object_property_get_int(Object *obj, const char *name,return retval; } +typedef struct EnumProperty { +const char *const *strings; +int (*get)(Object *,Error **); +void (*set)(Object *,int ,Error **); +}EnumProperty ; + +int object_property_get_enum (Object *obj,const char *name, -const char *const strings[],Error **errp) +Error **errp) {StringOutputVisitor *sov;StringInputVisitor *siv;
qom/object.c int64_t object_property_get_int(Object *obj, const char *name, · line 1048
Quoted 10 lines
qom/object.c +8
-0char *str;int ret; +ObjectProperty *prop =object_property_find (obj, name, errp); +EnumProperty *enumprop; + +if (prop ==NULL ) { +return 0 ; + } + +if (!g_str_equal (prop->type,"enum" )) {
Inspecting the type is a bit gross, and in fact it breaks if you keep the QAPI type name in patch 6. The HostMemPolicy conversion is certainly okay, but unfortunately this is not.
If you decide to go with macros in patch 6, perhaps you can do the same here so that object_property_get_enum(obj, "policy", HostMemPolicy, &err) can compare against the "HostMemPolicy" type.
The fact that HostMemPolicy_lookup exists is then a (mild, but sufficient) guarantee that the opaque pointer of the property is really an EnumProperty pointer.
Paolo
Quoted 33 lines
qom/object.c +8
-7+error_setg (errp,"Property %s on %s is not an 'enum' type" , + name,object_class_get_name ( +object_get_class (obj))); +return 0 ; + } + + enumprop = prop->opaque; sov =string_output_visitor_new (false);object_property_get (obj,string_output_get_visitor (sov), name, errp);@@ -1038,7 +1060,7 @@ int object_property_get_enum(Object *obj, const char *name, siv =string_input_visitor_new (str);string_output_visitor_cleanup (sov);visit_type_enum (string_input_get_visitor (siv), - &ret, strings,NULL , name, errp); + &ret, enumprop->strings,NULL , name, errp);g_free (str);string_input_visitor_cleanup (siv);@@ -1607,12 +1629,6 @@ void object_property_add_bool(Object *obj, const char *name, } } -typedef struct EnumProperty { -const char *const *strings; -int (*get)(Object *,Error **); -void (*set)(Object *,int ,Error **); -}EnumProperty ; -static void property_get_enum (Object *obj,Visitor *v,void *opaque,const char *name,Error **errp) {
RFC PATCH 8/34 crypto: introduce new module for computing hash digests
In reply to Daniel P. BerrangeIntroduce a new crypto/ directory that will (eventually) contain all the cryptographic related code. This initially defines a wrapper for initializing gnutls and for computing hashes with gnutls. The former ensures that gnutls is guaranteed to be initialized exactly once in QEMU regardless of CLI args. The block quorum code currently fails to initialize gnutls so it only works by luck, if VNC server TLS is not requested. The hash APIs avoids the need to litter the rest of the code with preprocessor checks and simplifies callers by allocating the correct amount of memory for the requested hash.
--- Makefile.objs | 1 + configure | 46 +++++++++++ crypto/Makefile.objs | 2 + crypto/hash.c | 202 +++++++++++++++++++++++++++++++++++++++++++++ crypto/init.c | 62 ++++++++++++++ include/crypto/hash.h | 189 ++++++++++++++++++++++++++++++++++++++++++ include/crypto/init.h | 29 +++++++ tests/.gitignore | 1 + tests/Makefile | 2 + tests/test-crypto-hash.c | 209 +++++++++++++++++++++++++++++++++++++++++++++++ vl.c | 8 ++ 11 files changed, 751 insertions(+) create mode 100644 crypto/Makefile.objs create mode 100644 crypto/hash.c create mode 100644 crypto/init.c create mode 100644 include/crypto/hash.h create mode 100644 include/crypto/init.h create mode 100644 tests/test-crypto-hash.c
Makefile.objs +1 -0
@@ -2,6 +2,7 @@ # Common libraries for tools and emulators stub-obj-y = stubs/ util-obj-y = util/ qobject/ qapi/ qapi-types.o qapi-visit.o qapi-event.o +util-obj-y += crypto/ ####################################################################### # block-obj-y is code used by both qemu system emulation and qemu-img
configure +46 -0
82 lines not on this page · raw message
crypto/Makefile.objs new file +2 -0
@@ -0,0 +1,2 @@ +util-obj-y += init.o +util-obj-y += hash.o
crypto/hash.c new file +202 -0
202 lines not on this page · raw message
crypto/init.c new file +62 -0
62 lines not on this page · raw message
include/crypto/hash.h new file +189 -0
189 lines not on this page · raw message
include/crypto/init.h new file +29 -0
@@ -0,0 +1,29 @@ +/* +* QEMU Crypto initialization +* +* Copyright (c) 2015 Red Hat, Inc. +* +* This library is free software; you can redistribute it and/or +* modify it under the terms of the GNU Lesser General Public +* License as published by the Free Software Foundation; either +* version 2 of the License, or (at your option) any later version. +* +* This library is distributed in the hope that it will be useful, +* but WITHOUT ANY WARRANTY; without even the implied warranty of +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +* Lesser General Public License for more details. +* +* You should have received a copy of the GNU Lesser General Public +* License along with this library; if not, see <http://www.gnu.org/licenses/>. +* +*/ + +#ifndef QCRYPTO_INIT_H__ +#define QCRYPTO_INIT_H__ + +#include "qemu-common.h" +#include "qapi/error.h" + +int qcrypto_init (Error **errp); + +#endif /* QCRYPTO_INIT_H__ */
tests/.gitignore +1 -0
@@ -9,6 +9,7 @@ rcutorture test-aio test-bitops test-coroutine +test-crypto-hash test-cutils test-hbitmap test-int128
tests/Makefile +2 -0
@@ -72,6 +72,7 @@ check-unit-y += tests/test-qemu-opts$(EXESUF) gcov-files-test-qemu-opts-y = qom/test-qemu-opts.c check-unit-y += tests/test-write-threshold$(EXESUF) gcov-files-test-write-threshold-y = block/write-threshold.c +check-unit-$(CONFIG_GNUTLS_HASH) += tests/test-crypto-hash$(EXESUF) check-block-$(CONFIG_POSIX) += tests/qemu-iotests-quick.sh
@@ -309,6 +310,7 @@ tests/test-opts-visitor$(EXESUF): tests/test-opts-visitor.o $(test-qapi-obj-y) l tests/test-mul64$(EXESUF): tests/test-mul64.o libqemuutil.a tests/test-bitops$(EXESUF): tests/test-bitops.o libqemuutil.a +tests/test-crypto-hash$(EXESUF): tests/test-crypto-hash.o libqemuutil.a libqemustub.a libqos-obj-y = tests/libqos/pci.o tests/libqos/fw_cfg.o tests/libqos/malloc.o libqos-obj-y += tests/libqos/i2c.o tests/libqos/libqos.o
tests/test-crypto-hash.c new file +209 -0
209 lines not on this page · raw message
vl.c +8 -0
@@ -119,6 +119,7 @@ int main(int argc, char **argv)#include "qapi/opts-visitor.h" #include "qom/object_interfaces.h" #include "qapi-event.h" +#include "crypto/init.h" #define DEFAULT_RAM_SIZE 128
@@ -2787,6 +2788,7 @@ int main(int argc, char **argv, char **envp)uint64_t ram_slots =0 ;FILE *vmstate_dump_file =NULL ;Error *main_loop_err =NULL ; +Error *err =NULL ;qemu_init_cpu_loop ();qemu_mutex_lock_iothread ();
@@ -2829,6 +2831,12 @@ int main(int argc, char **argv, char **envp)runstate_init (); +if (qcrypto_init (&err) <0 ) { +fprintf (stderr,"Cannot initialize crypto: %s\n" , +error_get_pretty (err)); +error_free (err); +exit (1 ); + } rtc_clock =QEMU_CLOCK_HOST ;QLIST_INIT (&vm_change_state_head);
Signature · 1 line
2.1.0
Re: [Qemu-devel] [PATCH v1 RFC 08/34] crypto: introduce new module for computing hash digests
Quoted 75 lines · Daniel P. Berrange
Introduce a new
crypto/directory that will (eventually) contain all the cryptographic related code. This initially defines a wrapper for initializing gnutls and for computing hashes with gnutls. The former ensures that gnutls is guaranteed to be initialized exactly once in QEMU regardless of CLI args. The block quorum code currently fails to initialize gnutls so it only works by luck, if VNC server TLS is not requested. The hash APIs avoids the need to litter the rest of the code with preprocessor checks and simplifies callers by allocating the correct amount of memory for the requested hash.Signed-off-by Daniel P. Berrange <[email protected]>--- Makefile.objs | 1 + configure | 46 +++++++++++ crypto/Makefile.objs | 2 + crypto/hash.c | 202 +++++++++++++++++++++++++++++++++++++++++++++ crypto/init.c | 62 ++++++++++++++ include/crypto/hash.h | 189 ++++++++++++++++++++++++++++++++++++++++++ include/crypto/init.h | 29 +++++++ tests/.gitignore | 1 + tests/Makefile | 2 + tests/test-crypto-hash.c | 209 +++++++++++++++++++++++++++++++++++++++++++++++ vl.c | 8 ++ 11 files changed, 751 insertions(+)crypto/init.c new file +43
-0+ +#include "crypto/init.h" + +#include <glib/gi18n.h> + +#ifdef CONFIG_GNUTLS +#include <gnutls/gnutls.h> +#include <gnutls/crypto.h> + +/* #define DEBUG_GNUTLS */ + +#ifdef DEBUG_GNUTLS +static void qcrypto_gnutls_log (int level,const char *str) +{ +fprintf (stderr,"%d: %s" , level, str); +} +#endif + +int qcrypto_init (Error **errp) +{ +int ret; + ret =gnutls_global_init (); +if (ret <0 ) { +error_setg (errp, +_ ("Unable to initialize GNUTLS library: %s" ), +gnutls_strerror (ret)); +return -1 ; + } +#ifdef DEBUG_GNUTLS +gnutls_global_set_log_level (10 ); +gnutls_global_set_log_function (qcrypto_gnutls_log); +#endif +return 0 ; +} + +#else /* ! CONFIG_GNUTLS */ + +int qcrypto_init(Error **errp G_GNUC_UNUSED) +{ +return 0 ; +} + +#endif /* ! CONFIG_GNUTLS */
[snip]
Quoted 23 lines
vl.c +2
-0@@ -119,6 +119,7 @@ int main(int argc, char **argv)#include "qapi/opts-visitor.h" #include "qom/object_interfaces.h" #include "qapi-event.h" +#include "crypto/init.h" #define DEFAULT_RAM_SIZE 128@@ -2787,6 +2788,7 @@ int main(int argc, char **argv, char **envp)uint64_t ram_slots =0 ;FILE *vmstate_dump_file =NULL ;Error *main_loop_err =NULL ; +Error *err =NULL ;qemu_init_cpu_loop ();qemu_mutex_lock_iothread ();@@ -2829,6 +2831,12 @@ int main(int argc, char **argv, char **envp)runstate_init ();
vl.c int main(int argc, char **argv, char **envp) · line 2842
Quoted 10 lines
vl.c +6
-0+if (qcrypto_init (&err) <0 ) { +fprintf (stderr,"Cannot initialize crypto: %s\n" , +error_get_pretty (err)); +error_free (err); +exit (1 ); + } rtc_clock =QEMU_CLOCK_HOST ;QLIST_INIT (&vm_change_state_head);
I created the 'qcrypto_init()' method in this patch so that we have a single clear place to initialize gnutls - currently we are doing initialization only in VNC when TLS is requested. This won't fly when more areas of the code will use GNUTLS APIs.
It occurs to me though that this patch is either wrong or incomplete, as I'm only calling qcrypto_init() from vl.c. I would also need to ensure it is invoked from qemu-io, qemu-img, qemu-nbd, and all the test suite programs that use libqemutil.la too.
I'm thinking perhaps a better approach could be for the crypto related APIs to call qcrypto_init() on an as-needed basis. The downside would be that this could delay the point at which the user sees a gnutls initialization failure to only after QEMU has been running for a while, instead of being upfront at startup. The plus side is obviously that we'd not need to update every binary program main() method.
I notice though that QEMU does not make use of pthread_once() for global initializers. Is there any particular reason for this ? With this crypto code it is not safe to rely on being single threaded, since the crypto code can be invoked from I/O threads as well as the main event loop. So ideally I would use a pthread_once() instead of having a static 'bool is_initialized' protected by a pthread_mutex
Regards,
Daniel
Signature · 4 lines
|: http://berrange.com -o- http://www.flickr.com/photos/dberrange/ :| |: http://libvirt.org -o- http://virt-manager.org :| |: http://autobuild.org -o- http://search.cpan.org/~danberr/ :| |: http://entangle-photo.org -o- http://live.gnome.org/gtk-vnc :|
Re: [Qemu-devel] [PATCH v1 RFC 08/34] crypto: introduce new module for computing hash digests
Quoted 7 lines · Daniel P. Berrange
I'm thinking perhaps a better approach could be for the crypto related APIs to call
qcrypto_init()on an as-needed basis. The downside would be that this could delay the point at which the user sees a gnutls initialization failure to only after QEMU has been running for a while, instead of being upfront at startup. The plus side is obviously that we'd not need to update every binary programmain()method.
Quoted 6 lines
I notice though that QEMU does not make use of
pthread_once()for global initializers. Is there any particular reason for this ? With this crypto code it is not safe to rely on being single threaded, since the crypto code can be invoked from I/O threads as well as the main event loop. So ideally I would use apthread_once()instead of having a static 'boolis_initialized' protected by apthread_mutex
Yes---though you'd use a GOnce instead for portability.
In fact you would likely keep both, and use a GOnce from qcrypto_init. Then you can always call qcrypto_init from main if that helps making early failures clearer.
Paolo
Re: [Qemu-devel] [PATCH v1 RFC 08/34] crypto: introduce new module for computing hash digests
Quoted 17 lines · Paolo Bonzini
Quoted 7 lines · Daniel P. Berrange
I'm thinking perhaps a better approach could be for the crypto related APIs to call
qcrypto_init()on an as-needed basis. The downside would be that this could delay the point at which the user sees a gnutls initialization failure to only after QEMU has been running for a while, instead of being upfront at startup. The plus side is obviously that we'd not need to update every binary programmain()method.Quoted 6 lines
I notice though that QEMU does not make use of
pthread_once()for global initializers. Is there any particular reason for this ? With this crypto code it is not safe to rely on being single threaded, since the crypto code can be invoked from I/O threads as well as the main event loop. So ideally I would use apthread_once()instead of having a static 'boolis_initialized' protected by apthread_mutex
Quoted 5 lines
Yes---though you'd use a GOnce instead for portability.
In fact you would likely keep both, and use a GOnce from
qcrypto_init. Then you can always callqcrypto_initfrom main if that helps making early failures clearer.
Ok, I'll go with GOnce and keep an init call in vl.c for clear error reporting.
Regards,
Daniel
Signature · 4 lines
|: http://berrange.com -o- http://www.flickr.com/photos/dberrange/ :| |: http://libvirt.org -o- http://virt-manager.org :| |: http://autobuild.org -o- http://search.cpan.org/~danberr/ :| |: http://entangle-photo.org -o- http://live.gnome.org/gtk-vnc :|
RFC PATCH 9/34 crypto: move built-in AES implementation into crypto/
In reply to Daniel P. BerrangeTo prepare for a generic internal cipher API, move the built-in AES implementation into the crypto/ directory
---
block/qcow.c | 2 +-
block/qcow2.c | 1 -
block/qcow2.h | 2 +-
crypto/Makefile.objs | 1 +
{util => crypto}/aes.c | 2 +-
include/{qemu => crypto}/aes.h | 0
target-arm/crypto_helper.c | 2 +-
target-i386/fpu_helper.c | 1 -
target-i386/ops_sse.h | 2 +-
target-ppc/int_helper.c | 2 +-
util/Makefile.objs | 2 +-
11 files changed, 8 insertions(+), 9 deletions(-)
rename {util => crypto}/aes.c (99%)
rename include/{qemu => crypto}/aes.h (100%)block/qcow.c +1 -1
@@ -25,7 +25,7 @@#include "block/block_int.h" #include "qemu/module.h" #include <zlib.h> -#include "qemu/aes.h" +#include "crypto/aes.h" #include "migration/migration.h" /**************************************************************/
block/qcow2.c +0 -1
@@ -25,7 +25,6 @@#include "block/block_int.h" #include "qemu/module.h" #include <zlib.h> -#include "qemu/aes.h" #include "block/qcow2.h" #include "qemu/error-report.h" #include "qapi/qmp/qerror.h"
block/qcow2.h +1 -1
@@ -25,7 +25,7 @@#ifndef BLOCK_QCOW2_H #define BLOCK_QCOW2_H -#include "qemu/aes.h" +#include "crypto/aes.h" #include "block/coroutine.h" //#define DEBUG_ALLOC
crypto/Makefile.objs +1 -0
@@ -1,2 +1,3 @@ util-obj-y += init.o util-obj-y += hash.o +util-obj-y += aes.o
crypto/aes.c renamed from util/aes.c +1 -1
@@ -28,7 +28,7 @@* EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ #include "qemu-common.h" -#include "qemu/aes.h" +#include "crypto/aes.h" typedef uint32_t u32 ;typedef uint8_t u8 ;
include/crypto/aes.h renamed from include/qemu/aes.h +0 -0
target-arm/crypto_helper.c +1 -1
@@ -14,7 +14,7 @@#include "cpu.h" #include "exec/exec-all.h" #include "exec/helper-proto.h" -#include "qemu/aes.h" +#include "crypto/aes.h" union CRYPTO_STATE {uint8_t bytes[16 ];
target-i386/fpu_helper.c +0 -1
@@ -20,7 +20,6 @@#include <math.h> #include "cpu.h" #include "exec/helper-proto.h" -#include "qemu/aes.h" #include "qemu/host-utils.h" #include "exec/cpu_ldst.h"
target-i386/ops_sse.h +1 -1
@@ -18,7 +18,7 @@* License along with this library; if not, see <http://www.gnu.org/licenses/>. */ -#include "qemu/aes.h" +#include "crypto/aes.h" #if SHIFT ==0 #define Reg MMXReg
target-ppc/int_helper.c +1 -1
@@ -19,7 +19,7 @@#include "cpu.h" #include "qemu/host-utils.h" #include "exec/helper-proto.h" -#include "qemu/aes.h" +#include "crypto/aes.h" #include "helper_regs.h" /*****************************************************************************/
util/Makefile.objs +1 -1
@@ -9,7 +9,7 @@ util-obj-y += acl.o util-obj-y += error.o qemu-error.o util-obj-$(CONFIG_POSIX) += compatfd.o util-obj-y += id.o -util-obj-y += iov.o aes.o qemu-config.o qemu-sockets.o uri.o notify.o +util-obj-y += iov.o qemu-config.o qemu-sockets.o uri.o notify.o util-obj-y += qemu-option.o qemu-progress.o util-obj-y += hexdump.o util-obj-y += crc32c.o
Signature · 1 line
2.1.0
RFC PATCH 10/34 crypto: move built-in D3DES implementation into crypto/
In reply to Daniel P. BerrangeTo prepare for a generic internal cipher API, move the built-in D3DES implementation into the crypto/ directory.
This is not in fact a normal D3DES implementation, it is D3DES with double & triple length modes removed, and the key bytes in reversed bit order. IOW it is crippled specifically for the "benefit" of RFB, so call the new files desrfb.c instead of d3des.c to make it clear that it isn't a generally useful impl.
--- crypto/Makefile.objs | 1 + ui/d3des.c => crypto/desrfb.c | 2 +- ui/d3des.h => include/crypto/desrfb.h | 0 ui/Makefile.objs | 2 +- ui/vnc.c | 2 +- 5 files changed, 4 insertions(+), 3 deletions(-) rename ui/d3des.c => crypto/desrfb.c (99%) rename ui/d3des.h => include/crypto/desrfb.h (100%)
crypto/Makefile.objs +1 -0
@@ -1,3 +1,4 @@ util-obj-y += init.o util-obj-y += hash.o util-obj-y += aes.o +util-obj-y += desrfb.o
crypto/desrfb.c renamed from ui/d3des.c +1 -1
@@ -26,7 +26,7 @@* (GEnie : OUTER; CIS : [71755,204]) Graven Imagery, 1992. */ -#include "d3des.h" +#include "crypto/desrfb.h" static void scrunch (unsigned char *,unsigned long *);static void unscrun (unsigned long *,unsigned char *);
include/crypto/desrfb.h renamed from ui/d3des.h +0 -0
ui/Makefile.objs +1 -1
@@ -1,4 +1,4 @@ -vnc-obj-y += vnc.o d3des.o +vnc-obj-y += vnc.o vnc-obj-y += vnc-enc-zlib.o vnc-enc-hextile.o vnc-obj-y += vnc-enc-tight.o vnc-palette.o vnc-obj-y += vnc-enc-zrle.o
ui/vnc.c +1 -1
@@ -46,7 +46,7 @@ static const struct timeval VNC_REFRESH_STATS = { 0, 500000 };static const struct timeval VNC_REFRESH_LOSSY = {2 ,0 };#include "vnc_keysym.h" -#include "d3des.h" +#include "crypto/desrfb.h" static QTAILQ_HEAD(, VncDisplay) vnc_displays =QTAILQ_HEAD_INITIALIZER (vnc_displays);
Signature · 1 line
2.1.0
RFC PATCH 11/34 crypto: introduce generic cipher API & built-in implementation
In reply to Daniel P. BerrangeIntroduce a generic cipher API and an implementation of it that supports only the built-in AES and DES-RFB algorithms.
The test suite checks the supported algorithms + modes to validate that every backend implementation is actually correctly complying with the specs.
--- crypto/Makefile.objs | 1 + crypto/cipher-builtin.c | 391 +++++++++++++++++++++++++++++++++++++++++++++ crypto/cipher.c | 23 +++ include/crypto/cipher.h | 205 ++++++++++++++++++++++++ tests/.gitignore | 1 + tests/Makefile | 2 + tests/test-crypto-cipher.c | 290 +++++++++++++++++++++++++++++++++ 7 files changed, 913 insertions(+) create mode 100644 crypto/cipher-builtin.c create mode 100644 crypto/cipher.c create mode 100644 include/crypto/cipher.h create mode 100644 tests/test-crypto-cipher.c
crypto/Makefile.objs +1 -0
@@ -2,3 +2,4 @@ util-obj-y += init.o util-obj-y += hash.o util-obj-y += aes.o util-obj-y += desrfb.o +util-obj-y += cipher.o
crypto/cipher-builtin.c new file +391 -0
391 lines not on this page · raw message
crypto/cipher.c new file +23 -0
@@ -0,0 +1,23 @@ +/* +* QEMU Crypto cipher algorithms +* +* Copyright (c) 2015 Red Hat, Inc. +* +* This library is free software; you can redistribute it and/or +* modify it under the terms of the GNU Lesser General Public +* License as published by the Free Software Foundation; either +* version 2 of the License, or (at your option) any later version. +* +* This library is distributed in the hope that it will be useful, +* but WITHOUT ANY WARRANTY; without even the implied warranty of +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +* Lesser General Public License for more details. +* +* You should have received a copy of the GNU Lesser General Public +* License along with this library; if not, see <http://www.gnu.org/licenses/>. +* +*/ + +#include "crypto/cipher.h" + +#include "crypto/cipher-builtin.c"
include/crypto/cipher.h new file +205 -0
205 lines not on this page · raw message
tests/.gitignore +1 -0
@@ -9,6 +9,7 @@ rcutorture test-aio test-bitops test-coroutine +test-crypto-cipher test-crypto-hash test-cutils test-hbitmap
tests/Makefile +2 -0
@@ -73,6 +73,7 @@ gcov-files-test-qemu-opts-y = qom/test-qemu-opts.c check-unit-y += tests/test-write-threshold$(EXESUF) gcov-files-test-write-threshold-y = block/write-threshold.c check-unit-$(CONFIG_GNUTLS_HASH) += tests/test-crypto-hash$(EXESUF) +check-unit-y += tests/test-crypto-cipher$(EXESUF) check-block-$(CONFIG_POSIX) += tests/qemu-iotests-quick.sh
@@ -311,6 +312,7 @@ tests/test-opts-visitor$(EXESUF): tests/test-opts-visitor.o $(test-qapi-obj-y) l tests/test-mul64$(EXESUF): tests/test-mul64.o libqemuutil.a tests/test-bitops$(EXESUF): tests/test-bitops.o libqemuutil.a tests/test-crypto-hash$(EXESUF): tests/test-crypto-hash.o libqemuutil.a libqemustub.a +tests/test-crypto-cipher$(EXESUF): tests/test-crypto-cipher.o libqemuutil.a libqemustub.a libqos-obj-y = tests/libqos/pci.o tests/libqos/fw_cfg.o tests/libqos/malloc.o libqos-obj-y += tests/libqos/i2c.o tests/libqos/libqos.o
tests/test-crypto-cipher.c new file +290 -0
290 lines not on this page · raw message
Signature · 1 line
2.1.0
RFC PATCH 12/34 crypto: add a gcrypt cipher implementation
In reply to Daniel P. BerrangeIf we are linking to gnutls already and gnutls is built against gcrypt, then we should use gcrypt as a cipher backend in preference to our built-in backend.
This will be used when linking against GNUTLS 1.x and many GNUTLS 2.x versions.
--- configure | 29 +++++++ crypto/cipher-gcrypt.c | 204 +++++++++++++++++++++++++++++++++++++++++++++++++ crypto/cipher.c | 4 + crypto/init.c | 90 ++++++++++++++++++++++ 4 files changed, 327 insertions(+) create mode 100644 crypto/cipher-gcrypt.c
configure +29 -0
@@ -2144,6 +2144,7 @@ fi ########################################## # GNUTLS probe +gnutls_gcrypt=no if test "$gnutls" != "no"; then if $pkg_config --exists "gnutls"; then gnutls_cflags=`$pkg_config --cflags gnutls`
@@ -2159,6 +2160,18 @@ if test "$gnutls" != "no"; then else gnutls_hash="no" fi + + if $pkg_config --exists 'gnutls >= 3.0'; then + gnutls_gcrypt=no + elif $pkg_config --exists 'gnutls >= 2.12'; then + case `$pkg_config --libs --static gnutls` in + *gcrypt*) gnutls_gcrypt=yes ;; + *nettle*) gnutls_gcrypt=no ;; + *) gnutls_gcrypt=yes ;; + esac + else + gnutls_gcrypt=yes + fi elif test "$gnutls" = "yes"; then feature_not_found "gnutls" "Install gnutls devel" else
@@ -2169,6 +2182,18 @@ else gnutls_hash="no" fi +if test "$gnutls_gcrypt" != "no"; then + if has "libgcrypt-config"; then + gcrypt_cflags=`libgcrypt-config --cflags` + gcrypt_libs=`libgcrypt-config --libs` + libs_softmmu="$gcrypt_libs $libs_softmmu" + libs_tools="$gcrypt_libs $libs_tools" + QEMU_CFLAGS="$QEMU_CFLAGS $gcrypt_cflags" + else + feature_not_found "gcrypt" "Install gcrypt devel" + fi +fi + ########################################## # VTE probe
@@ -4399,6 +4424,7 @@ echo "SDL support $sdl" echo "GTK support $gtk" echo "GNUTLS support $gnutls" echo "GNUTLS hash $gnutls_hash" +echo "GNUTLS gcrypt $gnutls_gcrypt" echo "VTE support $vte" echo "curses support $curses" echo "curl support $curl"
@@ -4756,6 +4782,9 @@ fi if test "$gnutls_hash" = "yes" ; then echo "CONFIG_GNUTLS_HASH=y" >> $config_host_mak fi +if test "$gnutls_gcrypt" = "yes" ; then + echo "CONFIG_GNUTLS_GCRYPT=y" >> $config_host_mak +fi if test "$vte" = "yes" ; then echo "CONFIG_VTE=y" >> $config_host_mak echo "VTE_CFLAGS=$vte_cflags" >> $config_host_mak
crypto/cipher-gcrypt.c new file +204 -0
204 lines not on this page · raw message
crypto/cipher.c +4 -0
@@ -20,4 +20,8 @@#include "crypto/cipher.h" +#ifdef CONFIG_GNUTLS_GCRYPT +#include "crypto/cipher-gcrypt.c" +#else #include "crypto/cipher-builtin.c" +#endif
crypto/init.c +90 -0
116 lines not on this page · raw message
Signature · 1 line
2.1.0
RFC PATCH 13/34 crypto: add a nettle cipher implementation
In reply to Daniel P. BerrangeIf we are linking to gnutls already and gnutls is built against nettle, then we should use nettle as a cipher backend in preference to our built-in backend.
This will be used when linking against some GNUTLS 2.x versions and all GNUTLS 3.x versions.
--- configure | 35 +++++++- crypto/cipher-nettle.c | 226 +++++++++++++++++++++++++++++++++++++++++++++++++ crypto/cipher.c | 4 + 3 files changed, 262 insertions(+), 3 deletions(-) create mode 100644 crypto/cipher-nettle.c
configure +32 -3
@@ -2145,6 +2145,7 @@ fi # GNUTLS probe gnutls_gcrypt=no +gnutls_nettle=no if test "$gnutls" != "no"; then if $pkg_config --exists "gnutls"; then gnutls_cflags=`$pkg_config --cflags gnutls`
@@ -2163,14 +2164,25 @@ if test "$gnutls" != "no"; then if $pkg_config --exists 'gnutls >= 3.0'; then gnutls_gcrypt=no + gnutls_nettle=yes elif $pkg_config --exists 'gnutls >= 2.12'; then case `$pkg_config --libs --static gnutls` in - *gcrypt*) gnutls_gcrypt=yes ;; - *nettle*) gnutls_gcrypt=no ;; - *) gnutls_gcrypt=yes ;; + *gcrypt*) + gnutls_gcrypt=yes + gnutls_nettle=no + ;; + *nettle*) + gnutls_gcrypt=no + gnutls_nettle=yes + ;; + *) + gnutls_gcrypt=yes + gnutls_nettle=no + ;; esac else gnutls_gcrypt=yes + gnutls_nettle=no fi elif test "$gnutls" = "yes"; then feature_not_found "gnutls" "Install gnutls devel"
@@ -2195,6 +2207,19 @@ if test "$gnutls_gcrypt" != "no"; then fi +if test "$gnutls_nettle" != "no"; then + if $pkg_config --exists "nettle"; then + nettle_cflags=`$pkg_config --cflags nettle` + nettle_libs=`$pkg_config --libs nettle` + libs_softmmu="$nettle_libs $libs_softmmu" + libs_tools="$nettle_libs $libs_tools" + QEMU_CFLAGS="$QEMU_CFLAGS $nettle_cflags" + else + feature_not_found "nettle" "Install nettle devel" + fi +fi + + ########################################## # VTE probe
@@ -4425,6 +4450,7 @@ echo "GTK support $gtk" echo "GNUTLS support $gnutls" echo "GNUTLS hash $gnutls_hash" echo "GNUTLS gcrypt $gnutls_gcrypt" +echo "GNUTLS nettle $gnutls_nettle" echo "VTE support $vte" echo "curses support $curses" echo "curl support $curl"
@@ -4785,6 +4811,9 @@ fi if test "$gnutls_gcrypt" = "yes" ; then echo "CONFIG_GNUTLS_GCRYPT=y" >> $config_host_mak fi +if test "$gnutls_nettle" = "yes" ; then + echo "CONFIG_GNUTLS_NETTLE=y" >> $config_host_mak +fi if test "$vte" = "yes" ; then echo "CONFIG_VTE=y" >> $config_host_mak echo "VTE_CFLAGS=$vte_cflags" >> $config_host_mak
crypto/cipher-nettle.c new file +226 -0
226 lines not on this page · raw message
crypto/cipher.c +4 -0
@@ -23,5 +23,9 @@#ifdef CONFIG_GNUTLS_GCRYPT #include "crypto/cipher-gcrypt.c" #else +#ifdef CONFIG_GNUTLS_NETTLE +#include "crypto/cipher-nettle.c" +#else #include "crypto/cipher-builtin.c" #endif +#endif
Signature · 1 line
2.1.0
RFC PATCH 14/34 crypto: introduce new module for handling TLS credentials
In reply to Daniel P. BerrangeIntroduce a QCryptoTLSCreds class to store TLS credentials, for use by later TLS session code. The class is setup as a user creatable object, so instance can be created/deleted via 'object-add' and 'object-del' QMP commands, or via the -object command line arg.
If the credentials cannot be initialized an error will be reported as a QMP reply, or on stderr respectively.
A later patch will update the VNC server to use this eg
qemu-system-x86_64 -object qcrypto-tls-creds,id=tls0,... \
-vnc 127.0.0.1:1,tls-creds=tls0--- crypto/Makefile.objs | 1 + crypto/init.c | 8 + crypto/tlscreds.c | 566 ++++++++++++++++++++++++++++++++++++++++++++++ include/crypto/tlscreds.h | 134 +++++++++++ qemu-options.hx | 30 +++ tests/Makefile | 4 +- 6 files changed, 741 insertions(+), 2 deletions(-) create mode 100644 crypto/tlscreds.c create mode 100644 include/crypto/tlscreds.h
crypto/Makefile.objs +1 -0
@@ -3,3 +3,4 @@ util-obj-y += hash.o util-obj-y += aes.o util-obj-y += desrfb.o util-obj-y += cipher.o +util-obj-y += tlscreds.o
crypto/init.c +8 -0
@@ -19,6 +19,7 @@*/ #include "crypto/init.h" +#include "crypto/tlscreds.h" #include "qemu/thread.h" #include <glib/gi18n.h>
@@ -139,6 +140,13 @@ int qcrypto_init(Error **errp)gcry_control (GCRYCTL_INITIALIZATION_FINISHED ,0 );#endif +/* XXX hack - if we don't reference any function in tlscreds.c +* then the linker drops tlscred.o from libqemutil.a when it +* links the emulators as it thinks it is unused. It isn't +* clever enough to see the constructor :-( +*/ +qcrypto_tls_creds_dummy (); +return 0 ; }
crypto/tlscreds.c new file +566 -0
566 lines not on this page · raw message
include/crypto/tlscreds.h new file +134 -0
134 lines not on this page · raw message
qemu-options.hx +30 -0
@@ -3495,6 +3495,36 @@ the @option{virtio-rng} device. The @option{chardev} parameter is the unique ID of a character device backend that provides the connection to the RNG daemon. +@item -object qcrypto-tls-cred,id=@var{id},credtype=@var{type},endpoint=@var{endpoint},dir=@var{/path/to/cred/dir},verify-peer=@var{on|off} + +Creates a TLS credentials object, which can be used to provide TLS +support on the VNC server and other network backends. The @option{id} +parameter is a unique ID which network backends will use to access +the credentials. The @option{credtype} parameter takes one of the +values @var{anon}, for anonymous credentials or @var{x509} for +x509 certificate credentials. The @option{endpoint} is either +@option{server} or @option{client} depending on whether the QEMU +network backend that uses the credentials will be acting as a client +or as a server. If @option{verify-peer} is enabled (the default) +then once the handshake is completed, the peer credentials will be +verified. With x509 certificates, this implies that the clients +must be provided with valid client certificates too. + +The @var{dir} parameter tells QEMU where to find the credential +files. For server endpoints, this directory may contain a file +@var{dh-params.pem} providing diffie-hellman parameters to use +for the TLS server. If the file is missing, QEMU will generate +a set of DH parameters at startup. This is a computationally +expensive operation that consumes random pool entropy, so it is +recommended that a persistent set of parameters be generated +upfront and saved. + +For x509 certificate credentials the directory will contain further files +providing the x509 certificates. The certificates must be stored +in PEM format, in filenames @var{ca-cert.pem}, @var{ca-crl.pem} (optional), +@var{server-cert.pem} (only servers), @var{server-key.pem} (only servers), +@var{client-cert.pem} (only clients), and @var{client-key.pem} (only clients). + @end table ETEXI
tests/Makefile +2 -2
@@ -311,8 +311,8 @@ tests/test-opts-visitor$(EXESUF): tests/test-opts-visitor.o $(test-qapi-obj-y) l tests/test-mul64$(EXESUF): tests/test-mul64.o libqemuutil.a tests/test-bitops$(EXESUF): tests/test-bitops.o libqemuutil.a -tests/test-crypto-hash$(EXESUF): tests/test-crypto-hash.o libqemuutil.a libqemustub.a -tests/test-crypto-cipher$(EXESUF): tests/test-crypto-cipher.o libqemuutil.a libqemustub.a +tests/test-crypto-hash$(EXESUF): tests/test-crypto-hash.o $(qom-core-obj) libqemuutil.a libqemustub.a +tests/test-crypto-cipher$(EXESUF): tests/test-crypto-cipher.o $(qom-core-obj) libqemuutil.a libqemustub.a libqos-obj-y = tests/libqos/pci.o tests/libqos/fw_cfg.o tests/libqos/malloc.o libqos-obj-y += tests/libqos/i2c.o tests/libqos/libqos.o
Signature · 1 line
2.1.0
RFC PATCH 15/34 crypto: add sanity checking of TLS credentials
In reply to Daniel P. BerrangeIf the administrator incorrectly sets up their x509 certificates, the errors seen at runtime during connection attempts are very obscure and difficult to diagnose. This adds support in the TLS credentials object to sanity check the certificates when QEMU first loads them. This gives the administrator immediate feedback for the majority of common configuration mistakes.
The code is derived from equivalent code that has been part of libvirt's TLS support for a while.
--- configure | 22 + crypto/tlscreds.c | 527 ++++++++++++++++++++ include/crypto/tlscreds.h | 1 + tests/.gitignore | 3 + tests/Makefile | 7 +- tests/crypto-tls-helpers.c | 485 +++++++++++++++++++ tests/crypto-tls-helpers.h | 133 +++++ tests/pkix_asn1_tab.c | 1103 ++++++++++++++++++++++++++++++++++++++++++ tests/test-crypto-tlscreds.c | 727 ++++++++++++++++++++++++++++ 9 files changed, 3007 insertions(+), 1 deletion(-) create mode 100644 tests/crypto-tls-helpers.c create mode 100644 tests/crypto-tls-helpers.h create mode 100644 tests/pkix_asn1_tab.c create mode 100644 tests/test-crypto-tlscreds.c
configure +22 -0
@@ -416,6 +416,9 @@ if test "$debug_info" = "yes"; then LDFLAGS="-g $LDFLAGS" fi +test_cflags="" +test_libs="" + # make source path absolute source_path=`cd "$source_path"; pwd`
@@ -2219,6 +2222,19 @@ if test "$gnutls_nettle" != "no"; then fi fi +########################################## +# libtasn1 - only for the TLS creds/session test suite + +tasn1=yes +if $pkg_config --exists "libtasn1"; then + tasn1_cflags=`$pkg_config --cflags libtasn1` + tasn1_libs=`$pkg_config --libs libtasn1` + test_cflags="$test_cflags $tasn1_cflags" + test_libs="$test_libs $tasn1_libs" +else + tasn1=no +fi + ########################################## # VTE probe
@@ -4451,6 +4467,7 @@ echo "GNUTLS support $gnutls" echo "GNUTLS hash $gnutls_hash" echo "GNUTLS gcrypt $gnutls_gcrypt" echo "GNUTLS nettle $gnutls_nettle" +echo "libtasn1 $tasn1" echo "VTE support $vte" echo "curses support $curses" echo "curl support $curl"
@@ -4814,6 +4831,9 @@ fi if test "$gnutls_nettle" = "yes" ; then echo "CONFIG_GNUTLS_NETTLE=y" >> $config_host_mak fi +if test "$tasn1" = "yes" ; then + echo "CONFIG_TASN1=y" >> $config_host_mak +fi if test "$vte" = "yes" ; then echo "CONFIG_VTE=y" >> $config_host_mak echo "VTE_CFLAGS=$vte_cflags" >> $config_host_mak
@@ -5141,6 +5161,8 @@ echo "EXESUF=$EXESUF" >> $config_host_mak echo "DSOSUF=$DSOSUF" >> $config_host_mak echo "LDFLAGS_SHARED=$LDFLAGS_SHARED" >> $config_host_mak echo "LIBS_QGA+=$libs_qga" >> $config_host_mak +echo "TEST_LIBS=$test_libs" >> $config_host_mak +echo "TEST_CFLAGS=$test_cflags" >> $config_host_mak echo "POD2MAN=$POD2MAN" >> $config_host_mak echo "TRANSLATE_OPT_CFLAGS=$TRANSLATE_OPT_CFLAGS" >> $config_host_mak if test "$gcov" = "yes" ; then
crypto/tlscreds.c +527 -0
551 lines not on this page · raw message
include/crypto/tlscreds.h +1 -0
@@ -122,6 +122,7 @@ struct QCryptoTLSCreds {gnutls_dh_params_t dh_params;#endif bool verifyPeer; +bool sanityCheck; };struct QCryptoTLSCredsClass {
tests/.gitignore +3 -0
@@ -11,6 +11,9 @@ test-bitops test-coroutine test-crypto-cipher test-crypto-hash +test-crypto-tlscreds +test-crypto-tlscreds-work/ +test-crypto-tlscreds-certdir/ test-cutils test-hbitmap test-int128
tests/Makefile +6 -1
@@ -74,6 +74,7 @@ check-unit-y += tests/test-write-threshold$(EXESUF) gcov-files-test-write-threshold-y = block/write-threshold.c check-unit-$(CONFIG_GNUTLS_HASH) += tests/test-crypto-hash$(EXESUF) check-unit-y += tests/test-crypto-cipher$(EXESUF) +check-unit-$(CONFIG_GNUTLS) += tests/test-crypto-tlscreds$(EXESUF) check-block-$(CONFIG_POSIX) += tests/qemu-iotests-quick.sh
@@ -313,6 +314,8 @@ tests/test-mul64$(EXESUF): tests/test-mul64.o libqemuutil.a tests/test-bitops$(EXESUF): tests/test-bitops.o libqemuutil.a tests/test-crypto-hash$(EXESUF): tests/test-crypto-hash.o $(qom-core-obj) libqemuutil.a libqemustub.a tests/test-crypto-cipher$(EXESUF): tests/test-crypto-cipher.o $(qom-core-obj) libqemuutil.a libqemustub.a +tests/test-crypto-tlscreds$(EXESUF): tests/test-crypto-tlscreds.o tests/crypto-tls-helpers.o tests/pkix_asn1_tab.o \ + crypto/tlscreds.o $(qom-core-obj) libqemuutil.a libqemustub.a libqos-obj-y = tests/libqos/pci.o tests/libqos/fw_cfg.o tests/libqos/malloc.o libqos-obj-y += tests/libqos/i2c.o tests/libqos/libqos.o
@@ -369,7 +372,7 @@ tests/usb-hcd-uhci-test$(EXESUF): tests/usb-hcd-uhci-test.o $(libqos-usb-obj-y) tests/usb-hcd-ehci-test$(EXESUF): tests/usb-hcd-ehci-test.o $(libqos-usb-obj-y) tests/usb-hcd-xhci-test$(EXESUF): tests/usb-hcd-xhci-test.o $(libqos-usb-obj-y) tests/pc-cpu-test$(EXESUF): tests/pc-cpu-test.o -tests/vhost-user-test$(EXESUF): tests/vhost-user-test.o qemu-char.o qemu-timer.o $(qtest-obj-y) +tests/vhost-user-test$(EXESUF): tests/vhost-user-test.o qemu-char.o qemu-timer.o $(qtest-obj-y) $(qom-core-obj) libqemuutil.a libqemustub.a tests/qemu-iotests/socket_scm_helper$(EXESUF): tests/qemu-iotests/socket_scm_helper.o tests/test-qemu-opts$(EXESUF): tests/test-qemu-opts.o libqemuutil.a libqemustub.a tests/test-write-threshold$(EXESUF): tests/test-write-threshold.o $(block-obj-y) libqemuutil.a libqemustub.a
@@ -377,6 +380,8 @@ tests/test-write-threshold$(EXESUF): tests/test-write-threshold.o $(block-obj-y) ifeq ($(CONFIG_POSIX),y) LIBS += -lutil endif +LIBS += $(TEST_LIBS) +CFLAGS += $(TEST_CFLAGS) # QTest rules
tests/crypto-tls-helpers.c new file +485 -0
485 lines not on this page · raw message
tests/crypto-tls-helpers.h new file +133 -0
133 lines not on this page · raw message
tests/pkix_asn1_tab.c new file +1103 -0
1,103 lines not on this page · raw message
tests/test-crypto-tlscreds.c new file +727 -0
629 lines not on this page · raw message
The rest of this diff is not shown. View the raw message.
Signature · 1 line
2.1.0
Part of the diff is not shown here. View the raw message.
RFC PATCH 16/34 crypto: introduce new module for handling TLS sessions
In reply to Daniel P. BerrangeIntroduce a QCryptoTLSSession object that will encapsulate all the code for setting up and using a client/sever TLS session. This isolates the code which depends on the gnutls library, avoiding #ifdefs in the rest of the codebase, as well as facilitating any possible future port to other TLS libraries, if desired.
--- crypto/Makefile.objs | 1 + crypto/tlssession.c | 546 +++++++++++++++++++++++++++++++++++++++++ include/crypto/tlssession.h | 313 +++++++++++++++++++++++ tests/.gitignore | 4 + tests/Makefile | 3 + tests/test-crypto-tlssession.c | 540 ++++++++++++++++++++++++++++++++++++++++ 6 files changed, 1407 insertions(+) create mode 100644 crypto/tlssession.c create mode 100644 include/crypto/tlssession.h create mode 100644 tests/test-crypto-tlssession.c
crypto/Makefile.objs +1 -0
@@ -4,3 +4,4 @@ util-obj-y += aes.o util-obj-y += desrfb.o util-obj-y += cipher.o util-obj-y += tlscreds.o +util-obj-y += tlssession.o
crypto/tlssession.c new file +546 -0
546 lines not on this page · raw message
include/crypto/tlssession.h new file +313 -0
313 lines not on this page · raw message
tests/.gitignore +4 -0
@@ -14,6 +14,10 @@ test-crypto-hash test-crypto-tlscreds test-crypto-tlscreds-work/ test-crypto-tlscreds-certdir/ +test-crypto-tlssession +test-crypto-tlssession-work/ +test-crypto-tlssession-client/ +test-crypto-tlssession-server/ test-cutils test-hbitmap test-int128
tests/Makefile +3 -0
@@ -75,6 +75,7 @@ gcov-files-test-write-threshold-y = block/write-threshold.c check-unit-$(CONFIG_GNUTLS_HASH) += tests/test-crypto-hash$(EXESUF) check-unit-y += tests/test-crypto-cipher$(EXESUF) check-unit-$(CONFIG_GNUTLS) += tests/test-crypto-tlscreds$(EXESUF) +check-unit-$(CONFIG_GNUTLS) += tests/test-crypto-tlssession$(EXESUF) check-block-$(CONFIG_POSIX) += tests/qemu-iotests-quick.sh
@@ -316,6 +317,8 @@ tests/test-crypto-hash$(EXESUF): tests/test-crypto-hash.o $(qom-core-obj) libqem tests/test-crypto-cipher$(EXESUF): tests/test-crypto-cipher.o $(qom-core-obj) libqemuutil.a libqemustub.a tests/test-crypto-tlscreds$(EXESUF): tests/test-crypto-tlscreds.o tests/crypto-tls-helpers.o tests/pkix_asn1_tab.o \ crypto/tlscreds.o $(qom-core-obj) libqemuutil.a libqemustub.a +tests/test-crypto-tlssession$(EXESUF): tests/test-crypto-tlssession.o tests/crypto-tls-helpers.o tests/pkix_asn1_tab.o \ + crypto/tlscreds.o crypto/tlssession.o $(qom-core-obj) libqemuutil.a libqemustub.a libqos-obj-y = tests/libqos/pci.o tests/libqos/fw_cfg.o tests/libqos/malloc.o libqos-obj-y += tests/libqos/i2c.o tests/libqos/libqos.o
tests/test-crypto-tlssession.c new file +540 -0
540 lines not on this page · raw message
Signature · 1 line
2.1.0
RFC PATCH 17/34 block: convert quorum blockdrv to use crypto APIs
In reply to Daniel P. BerrangeGet rid of direct use of gnutls APIs in quorum blockdrv in favour of using the crypto APIs. This avoids the need to do conditional compilation of the quorum driver. It can simply report an error at file open file instead if the required hash algorithm isn't supported by QEMU.
--- block/Makefile.objs | 2 +- block/quorum.c | 38 +++++++++++++++++++------------------- configure | 39 --------------------------------------- 3 files changed, 20 insertions(+), 59 deletions(-)
block/Makefile.objs +1 -1
@@ -3,7 +3,7 @@ block-obj-y += qcow2.o qcow2-refcount.o qcow2-cluster.o qcow2-snapshot.o qcow2-c block-obj-y += qed.o qed-gencb.o qed-l2-cache.o qed-table.o qed-cluster.o block-obj-y += qed-check.o block-obj-$(CONFIG_VHDX) += vhdx.o vhdx-endian.o vhdx-log.o -block-obj-$(CONFIG_QUORUM) += quorum.o +block-obj-y += quorum.o block-obj-y += parallels.o blkdebug.o blkverify.o block-obj-y += block-backend.o snapshot.o qapi.o block-obj-$(CONFIG_WIN32) += raw-win32.o win32-aio.o
block/quorum.c +19 -19
@@ -13,8 +13,6 @@* See the COPYING file in the top-level directory. */ -#include <gnutls/gnutls.h> -#include <gnutls/crypto.h> #include "block/block_int.h" #include "qapi/qmp/qbool.h" #include "qapi/qmp/qdict.h"
@@ -23,6 +21,9 @@#include "qapi/qmp/qlist.h" #include "qapi/qmp/qstring.h" #include "qapi-event.h" +#include "crypto/hash.h" + +#include <glib/gi18n.h> #define HASH_LENGTH 32
@@ -33,7 +34,7 @@/* This union holds a vote hash value */ typedef union QuorumVoteValue { -char h[HASH_LENGTH ];/* SHA-256 hash */ +uint8_t h[HASH_LENGTH ];/* SHA-256 hash */ int64_t l;/* simpler 64 bits hash */ }QuorumVoteValue ;
@@ -430,25 +431,18 @@ static void quorum_free_vote_list(QuorumVotes *votes)static int quorum_compute_hash (QuorumAIOCB *acb,int i,QuorumVoteValue *hash) { -int j, ret; -gnutls_hash_hd_t dig;QEMUIOVector *qiov = &acb->qcrs[i].qiov; - - ret =gnutls_hash_init (&dig,GNUTLS_DIG_SHA256 ); - -if (ret <0 ) { -return ret; +size_t len =sizeof (hash->h); +uint8_t *data = hash->h; + +if (qcrypto_hash_bytesv (QCRYPTO_HASH_ALG_SHA256 , + qiov->iov, qiov->niov, + &data, &len, +NULL ) <0 ) { +return -EINVAL ; } -for (j =0 ; j < qiov->niov; j++) { - ret =gnutls_hash (dig, qiov->iov[j].iov_base, qiov->iov[j].iov_len); -if (ret <0 ) { -break ; - } - } - -gnutls_hash_deinit (dig, (void *) hash); -return ret; +return 0 ; }static QuorumVoteVersion *quorum_get_vote_winner (QuorumVotes *votes)
@@ -875,6 +869,12 @@ static int quorum_open(BlockDriverState *bs, QDict *options, int flags,int i;int ret =0 ; +if (!qcrypto_hash_supports (QCRYPTO_HASH_ALG_SHA256 )) { +error_setg (errp,"%s" , +_ ("SHA256 hash support is required for quorum device" )); +return -EINVAL ; + } +qdict_flatten (options);qdict_extract_subqdict (options, &sub,"children." );qdict_array_split (sub, &list);
configure +0 -39
@@ -336,7 +336,6 @@ vte="" tpm="yes" libssh2="" vhdx="" -quorum="" numa="" # parse CC options first
@@ -1135,10 +1134,6 @@ for opt do ;; --disable-vhdx) vhdx="no" ;; - --disable-quorum) quorum="no" - ;; - --enable-quorum) quorum="yes" - ;; --disable-numa) numa="no" ;; --enable-numa) numa="yes"
@@ -1414,8 +1409,6 @@ Advanced options (experts only): --enable-libssh2 enable ssh block device support --disable-vhdx disable support for the Microsoft VHDX image format --enable-vhdx enable support for the Microsoft VHDX image format - --disable-quorum disable quorum block filter support - --enable-quorum enable quorum block filter support --disable-numa disable libnuma support --enable-numa enable libnuma support
@@ -2412,33 +2405,6 @@ EOF fi ########################################## -# Quorum probe (check for gnutls) -if test "$quorum" != "no" ; then -cat > $TMPC <<EOF -#include <gnutls/gnutls.h> -#include <gnutls/crypto.h> -int main(void) {char data[4096], digest[32]; -gnutls_hash_fast(GNUTLS_DIG_SHA256, data, 4096, digest); -return 0; -} -EOF -quorum_tls_cflags=`$pkg_config --cflags gnutls 2> /dev/null` -quorum_tls_libs=`$pkg_config --libs gnutls 2> /dev/null` -if compile_prog "$quorum_tls_cflags" "$quorum_tls_libs" ; then - qcow_tls=yes - libs_softmmu="$quorum_tls_libs $libs_softmmu" - libs_tools="$quorum_tls_libs $libs_softmmu" - QEMU_CFLAGS="$QEMU_CFLAGS $quorum_tls_cflags" - quorum="yes" -else - if test "$quorum" = "yes"; then - feature_not_found "gnutls" "gnutls > 2.10.0 required to compile Quorum" - fi - quorum="no" -fi -fi - -########################################## # VNC SASL detection if test "$vnc" = "yes" -a "$vnc_sasl" != "no" ; then cat > $TMPC <<EOF
@@ -4545,7 +4511,6 @@ echo "libssh2 support $libssh2" echo "TPM passthrough $tpm_passthrough" echo "QOM debugging $qom_cast_debug" echo "vhdx $vhdx" -echo "Quorum $quorum" echo "lzo support $lzo" echo "snappy support $snappy" echo "bzip2 support $bzip2"
@@ -5023,10 +4988,6 @@ if test "$libssh2" = "yes" ; then echo "LIBSSH2_LIBS=$libssh2_libs" >> $config_host_mak fi -if test "$quorum" = "yes" ; then - echo "CONFIG_QUORUM=y" >> $config_host_mak -fi - if test "$vhdx" = "yes" ; then echo "CONFIG_VHDX=y" >> $config_host_mak fi
Signature · 1 line
2.1.0
RFC PATCH 18/34 ui: convert VNC websockets to use crypto APIs
In reply to Daniel P. BerrangeRemove the direct use of gnutls for hash processing in the websockets code, in favour of using the crypto APIs. This allows the websockets code to be built unconditionally removing countless conditional checks from the VNC code.
--- configure | 19 +-------------- ui/Makefile.objs | 2 +- ui/vnc-ws.c | 22 ++++++++---------- ui/vnc-ws.h | 2 -- ui/vnc.c | 70 ++++++++++++-------------------------------------------- ui/vnc.h | 8 ------- 6 files changed, 26 insertions(+), 97 deletions(-)
configure +1 -18
@@ -245,7 +245,6 @@ vnc_tls="" vnc_sasl="" vnc_jpeg="" vnc_png="" -vnc_ws="" xen="" xen_ctrl_version="" xen_pci_passthrough=""
@@ -889,10 +888,6 @@ for opt do ;; --enable-vnc-png) vnc_png="yes" ;; - --disable-vnc-ws) vnc_ws="no" - ;; - --enable-vnc-ws) vnc_ws="yes" - ;; --disable-slirp) slirp="no" ;; --disable-uuid) uuid="no"
@@ -2376,7 +2371,7 @@ fi ########################################## # VNC TLS/WS detection -if test "$vnc" = "yes" -a \( "$vnc_tls" != "no" -o "$vnc_ws" != "no" \) ; then +if test "$vnc" = "yes" -a "$vnc_tls" != "no" ; then cat > $TMPC <<EOF #include <gnutls/gnutls.h> int main(void) { gnutls_session_t s; gnutls_init(&s, GNUTLS_SERVER); return 0; }
@@ -2387,20 +2382,13 @@ EOF if test "$vnc_tls" != "no" ; then vnc_tls=yes fi - if test "$vnc_ws" != "no" ; then - vnc_ws=yes - fi libs_softmmu="$vnc_tls_libs $libs_softmmu" QEMU_CFLAGS="$QEMU_CFLAGS $vnc_tls_cflags" else if test "$vnc_tls" = "yes" ; then feature_not_found "vnc-tls" "Install gnutls devel" fi - if test "$vnc_ws" = "yes" ; then - feature_not_found "vnc-ws" "Install gnutls devel" - fi vnc_tls=no - vnc_ws=no fi fi
@@ -4448,7 +4436,6 @@ if test "$vnc" = "yes" ; then echo "VNC SASL support $vnc_sasl" echo "VNC JPEG support $vnc_jpeg" echo "VNC PNG support $vnc_png" - echo "VNC WS support $vnc_ws" fi if test -n "$sparc_cpu"; then echo "Target Sparc Arch $sparc_cpu"
@@ -4653,10 +4640,6 @@ fi if test "$vnc_png" = "yes" ; then echo "CONFIG_VNC_PNG=y" >> $config_host_mak fi -if test "$vnc_ws" = "yes" ; then - echo "CONFIG_VNC_WS=y" >> $config_host_mak - echo "VNC_WS_CFLAGS=$vnc_ws_cflags" >> $config_host_mak -fi if test "$fnmatch" = "yes" ; then echo "CONFIG_FNMATCH=y" >> $config_host_mak fi
ui/Makefile.objs +1 -1
@@ -4,7 +4,7 @@ vnc-obj-y += vnc-enc-tight.o vnc-palette.o vnc-obj-y += vnc-enc-zrle.o vnc-obj-$(CONFIG_VNC_TLS) += vnc-tls.o vnc-auth-vencrypt.o vnc-obj-$(CONFIG_VNC_SASL) += vnc-auth-sasl.o -vnc-obj-$(CONFIG_VNC_WS) += vnc-ws.o +vnc-obj-y += vnc-ws.o vnc-obj-y += vnc-jobs.o common-obj-y += keymaps.o console.o cursor.o qemu-pixman.o
ui/vnc-ws.c +10 -12
@@ -20,6 +20,7 @@#include "vnc.h" #include "qemu/main-loop.h" +#include "crypto/hash.h" #ifdef CONFIG_VNC_TLS #include "qemu/sockets.h"
@@ -203,24 +204,21 @@ static char *vncws_extract_handshake_entry(const char *handshake,static void vncws_send_handshake_response (VncState *vs,const char * key) {char combined_key[WS_CLIENT_KEY_LEN +WS_GUID_LEN +1 ]; -unsigned char hash[SHA1_DIGEST_LEN ]; -size_t hash_size =sizeof (hash);char *accept =NULL , *response =NULL ; -gnutls_datum_t in; -int ret; +Error *err =NULL ;g_strlcpy (combined_key, key,WS_CLIENT_KEY_LEN +1 );g_strlcat (combined_key,WS_GUID ,WS_CLIENT_KEY_LEN +WS_GUID_LEN +1 );/* hash and encode it */ - in.data = (void *)combined_key; - in.size =WS_CLIENT_KEY_LEN +WS_GUID_LEN ; - ret =gnutls_fingerprint (GNUTLS_DIG_SHA1 , &in, hash, &hash_size); -if (ret ==GNUTLS_E_SUCCESS && hash_size <=SHA1_DIGEST_LEN ) { - accept =g_base64_encode (hash, hash_size); - } -if (accept ==NULL ) { -VNC_DEBUG ("Hashing Websocket combined key failed\n" ); +if (qcrypto_hash_base64 (QCRYPTO_HASH_ALG_SHA1 , + combined_key, +WS_CLIENT_KEY_LEN +WS_GUID_LEN , + &accept, + &err) <0 ) { +VNC_DEBUG ("Hashing Websocket combined key failed %s\n" , +error_get_pretty (err)); +error_free (err);vnc_client_error (vs);return ; }
ui/vnc-ws.h +0 -2
@@ -21,8 +21,6 @@#ifndef __QEMU_UI_VNC_WS_H#define __QEMU_UI_VNC_WS_H -#include <gnutls/gnutls.h> -#define B64LEN (__x) (((__x + 2) / 3) * 12 / 3)#define SHA1_DIGEST_LEN 20
ui/vnc.c +14 -56
230 lines not on this page · raw message
ui/vnc.h +0 -8
@@ -108,9 +108,7 @@ typedef struct VncDisplay VncDisplay;#ifdef CONFIG_VNC_SASL #include "vnc-auth-sasl.h" #endif -#ifdef CONFIG_VNC_WS #include "vnc-ws.h" -#endif struct VncRectStat {
@@ -156,10 +154,8 @@ struct VncDisplayint connections_limit;VncSharePolicy share_policy;int lsock; -#ifdef CONFIG_VNC_WS int lwebsock;bool ws_enabled; -#endif DisplaySurface *ds;DisplayChangeListener dcl;kbd_layout_t *kbd_layout;
@@ -294,21 +290,17 @@ struct VncState#ifdef CONFIG_VNC_SASL VncStateSASL sasl;#endif -#ifdef CONFIG_VNC_WS bool encode_ws;bool websocket; -#endif /* CONFIG_VNC_WS */ VncClientInfo *info;Buffer output;Buffer input; -#ifdef CONFIG_VNC_WS Buffer ws_input;Buffer ws_output;size_t ws_payload_remain;WsMask ws_payload_mask; -#endif /* current output mode information */ VncWritePixels *write_pixels;PixelFormat client_pf;
Signature · 1 line
2.1.0
RFC PATCH 19/34 block: convert qcow/qcow2 to use generic cipher API
In reply to Daniel P. BerrangeSwitch the qcow/qcow2 block driver over to use the generic cipher API, this allows it to use the pluggable AES implementations, instead of being hardcoded to use QEMU's built-in impl.
--- block/qcow.c | 100 ++++++++++++++++++++++++++++++++++++-------------- block/qcow2-cluster.c | 46 ++++++++++++++++++----- block/qcow2.c | 94 ++++++++++++++++++++++++----------------------- block/qcow2.h | 13 +++---- 4 files changed, 162 insertions(+), 91 deletions(-)
block/qcow.c +73 -27
@@ -25,7 +25,7 @@#include "block/block_int.h" #include "qemu/module.h" #include <zlib.h> -#include "crypto/aes.h" +#include "crypto/cipher.h" #include "migration/migration.h" /**************************************************************/
@@ -71,10 +71,8 @@ typedef struct BDRVQcowState {uint8_t *cluster_cache;uint8_t *cluster_data;uint64_t cluster_cache_offset; -uint32_t crypt_method;/* current crypt method, 0 if no key yet */ +QCryptoCipher *cipher;/* NULL if no key yet */ uint32_t crypt_method_header; -AES_KEY aes_encrypt_key; -AES_KEY aes_decrypt_key;CoMutex lock;Error *migration_blocker; } BDRVQcowState;
@@ -153,6 +151,11 @@ static int qcow_open(BlockDriverState *bs, QDict *options, int flags, ret = -EINVAL ; goto fail; } +if (!qcrypto_cipher_supports (QCRYPTO_CIPHER_ALG_AES )) { +error_setg (errp,"AES cipher not available" ); + ret = -EINVAL ; + goto fail; + } s->crypt_method_header = header.crypt_method;if (s->crypt_method_header) { bs->encrypted =1 ;
@@ -259,6 +262,7 @@ static int qcow_set_key(BlockDriverState *bs, const char *key)BDRVQcowState *s = bs->opaque;uint8_t keybuf[16 ];int len, i; +Error *err;memset (keybuf,0 ,16 ); len =strlen (key);
@@ -269,38 +273,66 @@ static int qcow_set_key(BlockDriverState *bs, const char *key)for (i =0 ;i < len;i++) { keybuf[i] = key[i]; } - s->crypt_method = s->crypt_method_header; -if (AES_set_encrypt_key (keybuf,128 , &s->aes_encrypt_key) !=0 ) -return -1 ; -if (AES_set_decrypt_key (keybuf,128 , &s->aes_decrypt_key) !=0 ) +if (s->cipher) { +qcrypto_cipher_free (s->cipher); + } + s->cipher =qcrypto_cipher_new ( +QCRYPTO_CIPHER_ALG_AES , +QCRYPTO_CIPHER_MODE_CBC , + keybuf,G_N_ELEMENTS (keybuf), + &err); + +if (!s->cipher) { +error_free (err);return -1 ; + }return 0 ; }/* The crypt function is compatible with the linux cryptoloop algorithm for < 4 GB images. NOTE: out_buf == in_buf is supported */ -static void encrypt_sectors (BDRVQcowState *s,int64_t sector_num, -uint8_t *out_buf,const uint8_t *in_buf, -int nb_sectors,int enc, -const AES_KEY *key) +static int encrypt_sectors (BDRVQcowState *s,int64_t sector_num, +uint8_t *out_buf,const uint8_t *in_buf, +int nb_sectors,bool enc,Error **errp) {union {uint64_t ll[2 ];uint8_t b[16 ]; } ivec;int i; +int ret;for (i =0 ; i < nb_sectors; i++) { ivec.ll[0 ] =cpu_to_le64 (sector_num); ivec.ll[1 ] =0 ; -AES_cbc_encrypt (in_buf, out_buf,512 , key, - ivec.b, enc); +if (qcrypto_cipher_setiv (s->cipher, + ivec.b,G_N_ELEMENTS (ivec.b), + errp) <0 ) { +return -1 ; + } +if (enc) { + ret =qcrypto_cipher_encrypt (s->cipher, + in_buf, + out_buf, +512 , + errp); + }else { + ret =qcrypto_cipher_decrypt (s->cipher, + in_buf, + out_buf, +512 , + errp); + } +if (ret <0 ) { +return -1 ; + } sector_num++; in_buf +=512 ; out_buf +=512 ; } +return 0 ; }/* 'allocate' is:
@@ -411,17 +443,22 @@ static uint64_t get_cluster_offset(BlockDriverState *bs,bdrv_truncate (bs->file, cluster_offset + s->cluster_size);/* if encrypted, we must initialize the cluster content which won't be written */ -if (s->crypt_method && +if (s->cipher && (n_end - n_start) < s->cluster_sectors) {uint64_t start_sect; start_sect = (offset & ~(s->cluster_size -1 )) >>9 ;memset (s->cluster_data +512 ,0x00 ,512 );for (i =0 ; i < s->cluster_sectors; i++) {if (i < n_start || i >= n_end) { -encrypt_sectors (s, start_sect + i, - s->cluster_data, - s->cluster_data +512 ,1 ,1 , - &s->aes_encrypt_key); +Error *err =NULL ; +if (encrypt_sectors (s, start_sect + i, + s->cluster_data, + s->cluster_data +512 ,1 , + true, &err) <0 ) { +error_free (err); + errno =EIO ; +return -1 ; + }if (bdrv_pwrite (bs->file, cluster_offset + i *512 , s->cluster_data,512 ) !=512 )return -1 ;
@@ -461,7 +498,7 @@ static int64_t coroutine_fn qcow_co_get_block_status(BlockDriverState *bs,if (!cluster_offset) {return 0 ; } -if ((cluster_offset &QCOW_OFLAG_COMPRESSED ) || s->crypt_method) { +if ((cluster_offset &QCOW_OFLAG_COMPRESSED ) || s->cipher) {return BDRV_BLOCK_DATA ; } cluster_offset |= (index_in_cluster <<BDRV_SECTOR_BITS );
@@ -528,6 +565,7 @@ static coroutine_fn int qcow_co_readv(BlockDriverState *bs, int64_t sector_num,QEMUIOVector hd_qiov;uint8_t *buf;void *orig_buf; +Error *err =NULL ;if (qiov->niov >1 ) { buf = orig_buf =qemu_try_blockalign (bs, qiov->size);
@@ -590,10 +628,11 @@ static coroutine_fn int qcow_co_readv(BlockDriverState *bs, int64_t sector_num,if (ret <0 ) {break ; } -if (s->crypt_method) { -encrypt_sectors (s, sector_num, buf, buf, - n,0 , - &s->aes_decrypt_key); +if (s->cipher) { +if (encrypt_sectors (s, sector_num, buf, buf, + n, false, &err) <0 ) { + goto fail; + } } } ret =0 ;
@@ -614,6 +653,7 @@ done:return ret; fail: +error_free (err); ret = -EIO ; goto done; }
@@ -661,12 +701,17 @@ static coroutine_fn int qcow_co_writev(BlockDriverState *bs, int64_t sector_num, ret = -EIO ;break ; } -if (s->crypt_method) { +if (s->cipher) { +Error *err =NULL ;if (!cluster_data) { cluster_data =g_malloc0 (s->cluster_size); } -encrypt_sectors (s, sector_num, cluster_data, buf, - n,1 , &s->aes_encrypt_key); +if (encrypt_sectors (s, sector_num, cluster_data, buf, + n, true, &err) <0 ) { +error_free (err); + ret = -EIO ; +break ; + } src_buf = cluster_data; }else { src_buf = buf;
@@ -703,6 +748,7 @@ static void qcow_close(BlockDriverState *bs) {BDRVQcowState *s = bs->opaque; +qcrypto_cipher_free (s->cipher);g_free (s->l1_table);qemu_vfree (s->l2_cache);g_free (s->cluster_cache);
block/qcow2-cluster.c +36 -10
72 lines not on this page · raw message
block/qcow2.c +48 -46
187 lines not on this page · raw message
block/qcow2.h +5 -8
@@ -25,7 +25,7 @@#ifndef BLOCK_QCOW2_H #define BLOCK_QCOW2_H -#include "crypto/aes.h" +#include "crypto/cipher.h" #include "block/coroutine.h" //#define DEBUG_ALLOC
@@ -250,10 +250,8 @@ typedef struct BDRVQcowState {CoMutex lock; -uint32_t crypt_method;/* current crypt method, 0 if no key yet */ +QCryptoCipher *cipher;/* current cipher, NULL if no key yet */ uint32_t crypt_method_header; -AES_KEY aes_encrypt_key; -AES_KEY aes_decrypt_key;uint64_t snapshots_offset;int snapshots_size;unsigned int nb_snapshots;
@@ -533,10 +531,9 @@ int qcow2_grow_l1_table(BlockDriverState *bs, uint64_t min_size,int qcow2_write_l1_entry (BlockDriverState *bs,int l1_index);void qcow2_l2_cache_reset (BlockDriverState *bs);int qcow2_decompress_cluster (BlockDriverState *bs,uint64_t cluster_offset); -void qcow2_encrypt_sectors (BDRVQcowState *s,int64_t sector_num, -uint8_t *out_buf,const uint8_t *in_buf, -int nb_sectors,int enc, -const AES_KEY *key); +int qcow2_encrypt_sectors (BDRVQcowState *s,int64_t sector_num, +uint8_t *out_buf,const uint8_t *in_buf, +int nb_sectors,bool enc,Error **errp);int qcow2_get_cluster_offset (BlockDriverState *bs,uint64_t offset,int *num,uint64_t *cluster_offset);
Signature · 1 line
2.1.0
RFC PATCH 20/34 ui: convert VNC to use generic cipher API
In reply to Daniel P. BerrangeSwitch the VNC server over to use the generic cipher API, this allows it to use the pluggable DES implementations, instead of being hardcoded to use QEMU's built-in impl.
--- ui/vnc.c | 52 +++++++++++++++++++++++++++++++++++++++++----------- 1 file changed, 41 insertions(+), 11 deletions(-)
ui/vnc.c +41 -11
@@ -47,7 +47,7 @@ static const struct timeval VNC_REFRESH_STATS = { 0, 500000 };static const struct timeval VNC_REFRESH_LOSSY = {2 ,0 };#include "vnc_keysym.h" -#include "crypto/desrfb.h" +#include "crypto/cipher.h" static QTAILQ_HEAD(, VncDisplay) vnc_displays =QTAILQ_HEAD_INITIALIZER (vnc_displays);
@@ -2515,9 +2515,11 @@ static void make_challenge(VncState *vs)static int protocol_client_auth_vnc (VncState *vs,uint8_t *data,size_t len) {unsigned char response[VNC_AUTH_CHALLENGE_SIZE ]; -int i, j, pwlen; +size_t i, pwlen;unsigned char key[8 ];time_t now =time (NULL ); +QCryptoCipher *cipher; +Error *err;if (!vs->vd->password) {VNC_DEBUG ("No password configured on server" );
@@ -2534,9 +2536,29 @@ static int protocol_client_auth_vnc(VncState *vs, uint8_t *data, size_t len) pwlen =strlen (vs->vd->password);for (i=0 ; i<sizeof (key); i++) key[i] = i<pwlen ? vs->vd->password[i] :0 ; -deskey (key,EN0 ); -for (j =0 ; j <VNC_AUTH_CHALLENGE_SIZE ; j +=8 ) -des (response+j, response+j); + + cipher =qcrypto_cipher_new ( +QCRYPTO_CIPHER_ALG_DES_RFB , +QCRYPTO_CIPHER_MODE_ECB , + key,G_N_ELEMENTS (key), + &err); +if (!cipher) { +VNC_DEBUG ("Cannot initialize cipher %s" , +error_get_pretty (err)); +error_free (err); + goto reject; + } + +if (qcrypto_cipher_decrypt (cipher, + vs->challenge, + response, +VNC_AUTH_CHALLENGE_SIZE , + &err) <0 ) { +VNC_DEBUG ("Cannot encrypt challenge %s" , +error_get_pretty (err)); +error_free (err); + goto reject; + }/* Compare expected vs actual challenge response */ if (memcmp (response, data,VNC_AUTH_CHALLENGE_SIZE ) !=0 ) {
@@ -3476,12 +3498,20 @@ void vnc_display_open(const char *id, Error **errp) } password =qemu_opt_get_bool (opts,"password" , false); -if (password &&fips_get_state ()) { -error_setg (errp, -"VNC password auth disabled due to FIPS mode, " -"consider using the VeNCrypt or SASL authentication " -"methods as an alternative" ); - goto fail; +if (password) { +if (fips_get_state ()) { +error_setg (errp, +"VNC password auth disabled due to FIPS mode, " +"consider using the VeNCrypt or SASL authentication " +"methods as an alternative" ); + goto fail; + } +if (!qcrypto_cipher_supports ( +QCRYPTO_CIPHER_ALG_DES_RFB )) { +error_setg (errp, +"Cipher backend does not support DES RFB algorithm" ); + goto fail; + } } reverse =qemu_opt_get_bool (opts,"reverse" , false);
Signature · 1 line
2.1.0
RFC PATCH 21/34 io: add abstract QIOChannel classes
In reply to Daniel P. BerrangeStart the new generic I/O channel framework by defining a QIOChannel abstract base class. This is designed to feel similar to GLib's GIOChannel, but with the addition of support for using iovecs, qemu error reporting, file descriptor passing and msg peeking.
--- Makefile.objs | 2 +- include/io/channel.h | 388 +++++++++++++++++++++++++++++++++++++++++++++++++++ io/Makefile.objs | 1 + io/channel.c | 178 +++++++++++++++++++++++ 4 files changed, 568 insertions(+), 1 deletion(-) create mode 100644 include/io/channel.h create mode 100644 io/Makefile.objs create mode 100644 io/channel.c
Makefile.objs +1 -1
@@ -2,7 +2,7 @@ # Common libraries for tools and emulators stub-obj-y = stubs/ util-obj-y = util/ qobject/ qapi/ qapi-types.o qapi-visit.o qapi-event.o -util-obj-y += crypto/ +util-obj-y += crypto/ io/ ####################################################################### # block-obj-y is code used by both qemu system emulation and qemu-img
include/io/channel.h new file +388 -0
388 lines not on this page · raw message
io/Makefile.objs new file +1 -0
@@ -0,0 +1 @@ +util-obj-y += channel.o
io/channel.c new file +178 -0
178 lines not on this page · raw message
Signature · 1 line
2.1.0
RFC PATCH 22/34 io: add helper module for creating watches on UNIX FDs
In reply to Daniel P. BerrangeA number of the channel implementations will require the ability to create watches on UNIX file descriptors. To avoid duplicating this code in each channel, provide a helper API for dealing with UNIX FDs. This code is explicitly not portable to the Windows platform, which will require its own set of helpers
--- include/io/channel-unix.h | 50 +++++++++++++++++++++++ io/Makefile.objs | 1 + io/channel-unix.c | 100 ++++++++++++++++++++++++++++++++++++++++++++++ 3 files changed, 151 insertions(+) create mode 100644 include/io/channel-unix.h create mode 100644 io/channel-unix.c
include/io/channel-unix.h new file +50 -0
@@ -0,0 +1,50 @@ +/* +* QEMU I/O channels UNIX platform helper APIs +* +* Copyright (c) 2015 Red Hat, Inc. +* +* This library is free software; you can redistribute it and/or +* modify it under the terms of the GNU Lesser General Public +* License as published by the Free Software Foundation; either +* version 2 of the License, or (at your option) any later version. +* +* This library is distributed in the hope that it will be useful, +* but WITHOUT ANY WARRANTY; without even the implied warranty of +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +* Lesser General Public License for more details. +* +* You should have received a copy of the GNU Lesser General Public +* License along with this library; if not, see <http://www.gnu.org/licenses/>. +* +*/ + +#ifndef QIO_CHANNEL_UNIX_H__ +#define QIO_CHANNEL_UNIX_H__ + +#include "io/channel.h" + +/* +* This module provides helper functions that will be needed by +* the various QIOChannel implementations, for performing UNIX +* platform specific tasks. In particular setting up UNIX file +* descriptors watches. +*/ + +/** +* @ioc: the channel object +* @fd: the UNIX file descriptor +* @condition: the I/O condition +* +* Create a new main loop source that is able to +* monitor the UNIX file descriptor @fd for the +* I/O conditions in @condition. This is able +* monitor block devices, character devices, +* sockets, pipes but not plain files. +* +* Returns: the new main loop source +*/ +GSource *qio_channel_unix_create_fd_watch (QIOChannel *ioc, +int fd, +GIOCondition condition); + +#endif /* QIO_CHANNEL_UNIX_H__ */
io/Makefile.objs +1 -0
@@ -1 +1,2 @@ util-obj-y += channel.o +util-obj-y += channel-unix.o
io/channel-unix.c new file +100 -0
100 lines not on this page · raw message
Signature · 1 line
2.1.0
RFC PATCH 23/34 io: add QIOChannelSocket class
In reply to Daniel P. BerrangeImplement a QIOChannel subclass that supports sockets I/O
TBD check errno handling of windows port & fix watch impl
--- include/io/channel-socket.h | 168 +++++++++++++ io/Makefile.objs | 1 + io/channel-socket.c | 572 ++++++++++++++++++++++++++++++++++++++++++++ 3 files changed, 741 insertions(+) create mode 100644 include/io/channel-socket.h create mode 100644 io/channel-socket.c
include/io/channel-socket.h new file +168 -0
@@ -0,0 +1,168 @@ +/* +* QEMU I/O channels sockets driver +* +* Copyright (c) 2015 Red Hat, Inc. +* +* This library is free software; you can redistribute it and/or +* modify it under the terms of the GNU Lesser General Public +* License as published by the Free Software Foundation; either +* version 2 of the License, or (at your option) any later version. +* +* This library is distributed in the hope that it will be useful, +* but WITHOUT ANY WARRANTY; without even the implied warranty of +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +* Lesser General Public License for more details. +* +* You should have received a copy of the GNU Lesser General Public +* License along with this library; if not, see <http://www.gnu.org/licenses/>. +* +*/ + +#ifndef QIO_CHANNEL_SOCKET_H__ +#define QIO_CHANNEL_SOCKET_H__ + +#include "io/channel.h" +#include "qemu/sockets.h" + +#define TYPE_QIO_CHANNEL_SOCKET "qemu:io-channel-socket" +#define QIO_CHANNEL_SOCKET (obj) \ + OBJECT_CHECK(QIOChannelSocket, (obj), TYPE_QIO_CHANNEL_SOCKET) + +typedef struct QIOChannelSocket QIOChannelSocket ; + +/** +* QIOChannelSocket: +* +* The QIOChannelSocket class provides a channel implementation +* that can transport data over a UNIX socket or TCP socket. +* Beyond the core channel API, it also provides functionality +* for accepting client connections, tuning some socket +* parameters and getting socket address strings. +*/ + +struct QIOChannelSocket { +QIOChannel parent; +int fd; +struct sockaddr_storage localAddr; +socklen_t localAddrLen; +struct sockaddr_storage remoteAddr; +socklen_t remoteAddrLen; +}; + + +/** +* qio_channel_socket_new_fd: +* @fd: the socket file descriptor +* @errp: pointer to an uninitialized error object +* +* Create a channel for performing I/O on the socket +* connection represented by the file descriptor @fd. +* +* Returns: the socket channel object, or NULL on error +*/ +QIOChannelSocket * +qio_channel_socket_new_fd (int fd, +Error **errp); + +/** +* qio_channel_socket_get_local_addr_string: +* @ioc: the socket channel object +* @hostname: pointer to be filled with hostname string +* @servicename: pointer to be filled with servicename string +* @family: pointer to be filled with network address family +* @errp: pointer to an uninitialized error object +* +* Get the string representation of the local socket +* address. The address information will be stored in +* the @hostname, @servicename and @family parameters. +* The @hostname and @servicename strings will be +* allocated to the size required and should be free +* with g_free() when no longer required +* +* Returns: 0 on success, -1 on error +*/ +int +qio_channel_socket_get_local_addr_string (QIOChannelSocket *ioc, +char **hostname, +char **servicename, +NetworkAddressFamily *family, +Error **errp); + +/** +* qio_channel_socket_get_remote_addr_string: +* @ioc: the socket channel object +* @hostname: pointer to be filled with hostname string +* @servicename: pointer to be filled with servicename string +* @family: pointer to be filled with network address family +* @errp: pointer to an uninitialized error object +* +* Get the string representation of the remote socket +* address. The address information will be stored in +* the @hostname, @servicename and @family parameters. +* The @hostname and @servicename strings will be +* allocated to the size required and should be free +* with g_free() when no longer required +* +* Returns: 0 on success, -1 on error +*/ +int +qio_channel_socket_get_remote_addr_string (QIOChannelSocket *ioc, +char **hostname, +char **servicename, +NetworkAddressFamily *family, +Error **errp); + +/** +* qio_channel_socket_set_nodelay: +* @ioc: the socket channel object +* @enabled: the new flag state +* +* Set the state of the NODELAY socket flag. If the +* @enabled parameter is true, then NODELAY will be +* set and data will be transmitted immediately. If +* @enabled is false, then data may be temporarily +* held for transmission to enable writes to be +* coallesced. +*/ +void +qio_channel_socket_set_nodelay (QIOChannelSocket *ioc, +bool enabled); + +/** +* qio_channel_socket_accept: +* @ioc: the socket channel object +* @errp: pointer to an uninitialized error object +* +* If the socket represents a server, then this accepts +* a new client connection. The returned channel will +* represent the connected client socket. +* +* Returns: the new client channel, or NULL on error +*/ +QIOChannelSocket * +qio_channel_socket_accept (QIOChannelSocket *ioc, +Error **errp); + +typedef enum { +QIO_CHANNEL_SOCKET_SHUTDOWN_BOTH , +QIO_CHANNEL_SOCKET_SHUTDOWN_READ , +QIO_CHANNEL_SOCKET_SHUTDOWN_WRITE , +}QIOChannelSocketShutdown ; + +/** +* qio_channel_socket_shutdown: +* @ioc: the socket channel object +* @how: the direction to shutdown +* @errp: pointer to an uninitialized error object +* +* Shutdowns transmission or receiving on a socket +* without closing the socket file descriptor. +* +* Returns: 0 on success, -1 on error +*/ +int +qio_channel_socket_shutdown (QIOChannelSocket *ioc, +QIOChannelSocketShutdown how, +Error **errp); + +#endif /* QIO_CHANNEL_SOCKET_H__ */
io/Makefile.objs +1 -0
@@ -1,2 +1,3 @@ util-obj-y += channel.o util-obj-y += channel-unix.o +util-obj-y += channel-socket.o
io/channel-socket.c new file +572 -0
572 lines not on this page · raw message
Signature · 1 line
2.1.0
Re: [Qemu-devel] [PATCH v1 RFC 23/34] io: add QIOChannelSocket class
Quoted 122 lines · Daniel P. Berrange
Implement a QIOChannel subclass that supports sockets I/O
TBD check errno handling of windows port & fix watch impl
Signed-off-by Daniel P. Berrange <[email protected]>--- include/io/channel-socket.h | 168 +++++++++++++ io/Makefile.objs | 1 + io/channel-socket.c | 572 ++++++++++++++++++++++++++++++++++++++++++++ 3 files changed, 741 insertions(+) create mode 100644 include/io/channel-socket.h create mode 100644 io/channel-socket.cinclude/io/channel-socket.h new file +103
-0@@ -0,0 +1,168 @@ +/* +* QEMU I/O channels sockets driver +* +* Copyright (c) 2015 Red Hat, Inc. +* +* This library is free software; you can redistribute it and/or +* modify it under the terms of the GNU Lesser General Public +* License as published by the Free Software Foundation; either +* version 2 of the License, or (at your option) any later version. +* +* This library is distributed in the hope that it will be useful, +* but WITHOUT ANY WARRANTY; without even the implied warranty of +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +* Lesser General Public License for more details. +* +* You should have received a copy of the GNU Lesser General Public +* License along with this library; if not, see <http://www.gnu.org/licenses/>. +* +*/ + +#ifndef QIO_CHANNEL_SOCKET_H__ +#define QIO_CHANNEL_SOCKET_H__ + +#include "io/channel.h" +#include "qemu/sockets.h" + +#define TYPE_QIO_CHANNEL_SOCKET "qemu:io-channel-socket" +#define QIO_CHANNEL_SOCKET (obj) \ + OBJECT_CHECK(QIOChannelSocket, (obj), TYPE_QIO_CHANNEL_SOCKET) + +typedef struct QIOChannelSocket QIOChannelSocket ; + +/** +* QIOChannelSocket: +* +* The QIOChannelSocket class provides a channel implementation +* that can transport data over a UNIX socket or TCP socket. +* Beyond the core channel API, it also provides functionality +* for accepting client connections, tuning some socket +* parameters and getting socket address strings. +*/ + +struct QIOChannelSocket { +QIOChannel parent; +int fd; +struct sockaddr_storage localAddr; +socklen_t localAddrLen; +struct sockaddr_storage remoteAddr; +socklen_t remoteAddrLen; +}; + + +/** +* qio_channel_socket_new_fd: +* @fd: the socket file descriptor +* @errp: pointer to an uninitialized error object +* +* Create a channel for performing I/O on the socket +* connection represented by the file descriptor @fd. +* +* Returns: the socket channel object, or NULL on error +*/ +QIOChannelSocket * +qio_channel_socket_new_fd (int fd, +Error **errp); + +/** +* qio_channel_socket_get_local_addr_string: +* @ioc: the socket channel object +* @hostname: pointer to be filled with hostname string +* @servicename: pointer to be filled with servicename string +* @family: pointer to be filled with network address family +* @errp: pointer to an uninitialized error object +* +* Get the string representation of the local socket +* address. The address information will be stored in +* the @hostname, @servicename and @family parameters. +* The @hostname and @servicename strings will be +* allocated to the size required and should be free +* with g_free() when no longer required +* +* Returns: 0 on success, -1 on error +*/ +int +qio_channel_socket_get_local_addr_string (QIOChannelSocket *ioc, +char **hostname, +char **servicename, +NetworkAddressFamily *family, +Error **errp); + +/** +* qio_channel_socket_get_remote_addr_string: +* @ioc: the socket channel object +* @hostname: pointer to be filled with hostname string +* @servicename: pointer to be filled with servicename string +* @family: pointer to be filled with network address family +* @errp: pointer to an uninitialized error object +* +* Get the string representation of the remote socket +* address. The address information will be stored in +* the @hostname, @servicename and @family parameters. +* The @hostname and @servicename strings will be +* allocated to the size required and should be free
include/io/channel-socket.h line 113
Quoted 10 lines
include/io/channel-socket.h new file +10
-0+* with g_free() when no longer required +* +* Returns: 0 on success, -1 on error +*/ +int +qio_channel_socket_get_remote_addr_string (QIOChannelSocket *ioc, +char **hostname, +char **servicename, +NetworkAddressFamily *family, +Error **errp);
Would it be possible to change these to use a SocketAddress* type?
include/io/channel-socket.h line 120
Quoted 7 lines
include/io/channel-socket.h new file +7
-0+ +/** +* qio_channel_socket_set_nodelay: +* @ioc: the socket channel object +* @enabled: the new flag state +* +* Set the state of the NODELAY socket flag. If the
The function name is okay, but please write TCP_NODELAY in the documentation, or talk about Nagle's algorithm instead of mentioning the flag.
Quoted 14 lines
include/io/channel-socket.h new file +14
-0+* @enabled parameter is true, then NODELAY will be +* set and data will be transmitted immediately. If +* @enabled is false, then data may be temporarily +* held for transmission to enable writes to be +* coallesced. +*/ +void +qio_channel_socket_set_nodelay (QIOChannelSocket *ioc, +bool enabled); + +/** +* qio_channel_socket_accept: +* @ioc: the socket channel object +* @errp: pointer to an uninitialized error object
include/io/channel-socket.h line 144
Quoted 10 lines
include/io/channel-socket.h new file +10
-0+* +* If the socket represents a server, then this accepts +* a new client connection. The returned channel will +* represent the connected client socket. +* +* Returns: the new client channel, or NULL on error +*/ +QIOChannelSocket * +qio_channel_socket_accept (QIOChannelSocket *ioc, +Error **errp);
Does it make sense for a passive socket to be a QIOChannelSocket? We have already a pretty decent API in util/qemu-sockets.c, and QIOChannelSocket will become more similar to qemu-sockets if you switch to SocketAddress. Perhaps this function can just take a file descriptor?
Paolo
Quoted 611 lines
include/io/channel-socket.h new file +24
-0+ +typedef enum { +QIO_CHANNEL_SOCKET_SHUTDOWN_BOTH , +QIO_CHANNEL_SOCKET_SHUTDOWN_READ , +QIO_CHANNEL_SOCKET_SHUTDOWN_WRITE , +}QIOChannelSocketShutdown ; + +/** +* qio_channel_socket_shutdown: +* @ioc: the socket channel object +* @how: the direction to shutdown +* @errp: pointer to an uninitialized error object +* +* Shutdowns transmission or receiving on a socket +* without closing the socket file descriptor. +* +* Returns: 0 on success, -1 on error +*/ +int +qio_channel_socket_shutdown (QIOChannelSocket *ioc, +QIOChannelSocketShutdown how, +Error **errp); + +#endif /* QIO_CHANNEL_SOCKET_H__ */ io/Makefile.objs +1
-0@@ -1,2 +1,3 @@ util-obj-y += channel.o util-obj-y += channel-unix.o +util-obj-y += channel-socket.oio/channel-socket.c new file +572
-0@@ -0,0 +1,572 @@ +/* +* QEMU I/O channels sockets driver +* +* Copyright (c) 2015 Red Hat, Inc. +* +* This library is free software; you can redistribute it and/or +* modify it under the terms of the GNU Lesser General Public +* License as published by the Free Software Foundation; either +* version 2 of the License, or (at your option) any later version. +* +* This library is distributed in the hope that it will be useful, +* but WITHOUT ANY WARRANTY; without even the implied warranty of +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +* Lesser General Public License for more details. +* +* You should have received a copy of the GNU Lesser General Public +* License along with this library; if not, see <http://www.gnu.org/licenses/>. +* +*/ + +#include <glib/gi18n.h> + +#include "io/channel-socket.h" +#include "io/channel-unix.h" + +#define SOCKET_MAX_FDS 16 + +static int +qio_channel_socket_get_addr_string (struct sockaddr_storage *sa, +socklen_t salen, +char **hostname, +char **servicename, +NetworkAddressFamily *family, +Error **errp) +{ +char host[NI_MAXHOST ]; +char serv[NI_MAXSERV ]; +int ret; + + ret =getnameinfo ((struct sockaddr *)sa, salen, + host,sizeof (host), + serv,sizeof (serv), +NI_NUMERICHOST |NI_NUMERICSERV ); +if (ret !=0 ) { +error_setg (errp,"Cannot format numeric socket address: %s\n" , +gai_strerror (ret)); +return -1 ; + } + + *hostname =g_strdup (host); + *servicename =g_strdup (serv); + *family =inet_netfamily (sa->ss_family); +return 0 ; +} + +int +qio_channel_socket_get_local_addr_string (QIOChannelSocket *ioc, +char **hostname, +char **servicename, +NetworkAddressFamily *family, +Error **errp) +{ +return qio_channel_socket_get_addr_string (&ioc->localAddr, + ioc->localAddrLen, + hostname, + servicename, + family, + errp); +} + +int +qio_channel_socket_get_remote_addr_string (QIOChannelSocket *ioc, +char **hostname, +char **servicename, +NetworkAddressFamily *family, +Error **errp) +{ +return qio_channel_socket_get_addr_string (&ioc->remoteAddr, + ioc->remoteAddrLen, + hostname, + servicename, + family, + errp); +} + +QIOChannelSocket * +qio_channel_socket_new_fd (int fd, +Error **errp) +{ +QIOChannelSocket *ioc; + + ioc =QIO_CHANNEL_SOCKET (object_new (TYPE_QIO_CHANNEL_SOCKET )); + + ioc->fd = fd; + ioc->remoteAddrLen =sizeof (ioc->remoteAddr); + ioc->localAddrLen =sizeof (ioc->localAddr); + +if (getpeername (fd, (struct sockaddr *)&ioc->remoteAddr, + &ioc->remoteAddrLen) <0 ) { +if (socket_error () ==ENOTCONN ) { +memset (&ioc->remoteAddr,0 ,sizeof (ioc->remoteAddr)); + ioc->remoteAddrLen =sizeof (ioc->remoteAddr); + }else { +error_setg_errno (errp,socket_error (),"%s" , +_ ("Unable to query remote socket address" )); + goto error; + } + } + +if (getsockname (fd, (struct sockaddr *)&ioc->localAddr, + &ioc->localAddrLen) <0 ) { +error_setg_errno (errp,socket_error (),"%s" , +_ ("Unable to query local socket address" )); + goto error; + } + +return ioc; + + error: + ioc->fd =-1 ;/* Let the caller close FD on failure */ +object_unref (OBJECT (ioc)); +return NULL ; +} + +QIOChannelSocket * +qio_channel_socket_accept (QIOChannelSocket *ioc, +Error **errp) +{ +QIOChannelSocket *cioc; + + cioc =QIO_CHANNEL_SOCKET (object_new (TYPE_QIO_CHANNEL_SOCKET )); + cioc->fd =-1 ; + cioc->remoteAddrLen =sizeof (ioc->remoteAddr); + cioc->localAddrLen =sizeof (ioc->localAddr); + + retry: + cioc->fd =accept (ioc->fd, (struct sockaddr *)&cioc->remoteAddr, + &cioc->remoteAddrLen); +if (cioc->fd <0 ) { +if (socket_error () ==EINTR ) { + goto retry; + } + goto error; + } + +if (getsockname (cioc->fd, (struct sockaddr *)&ioc->localAddr, + &ioc->localAddrLen) <0 ) { +error_setg_errno (errp,socket_error (),"%s" , +_ ("Unable to query local socket address" )); + goto error; + } + +return cioc; + + error: +object_unref (OBJECT (cioc)); +return NULL ; +} + +static void qio_channel_socket_init (Object *obj) +{ +QIOChannelSocket *ioc =QIO_CHANNEL_SOCKET (obj); + ioc->fd =-1 ; +} + +static void qio_channel_socket_finalize (Object *obj) +{ +QIOChannelSocket *ioc =QIO_CHANNEL_SOCKET (obj); +if (ioc->fd !=-1 ) { +close (ioc->fd); + ioc->fd =-1 ; + } +} + +static bool qio_channel_socket_has_feature (QIOChannel *ioc, +QIOChannelFeature feature) +{ +QIOChannelSocket *sioc =QIO_CHANNEL_SOCKET (ioc); + +switch (feature) { +case QIO_CHANNEL_FEATURE_FD_PASS : +#ifndef WIN32 +if (sioc->localAddr.ss_family ==AF_UNIX ) { +return true; + }else { +#endif /* WIN32 */ +return false; +#ifndef WIN32 + } +#endif /* WIN32 */ +default : +return false; + } +} + + +#ifndef WIN32 +static void qio_channel_socket_copy_fds (struct msghdr *msg, +int **fds,size_t *nfds) +{ +struct cmsghdr *cmsg; + + *nfds =0 ; + *fds =NULL ; + +for (cmsg =CMSG_FIRSTHDR (msg); cmsg; cmsg =CMSG_NXTHDR (msg, cmsg)) { +int fd_size, i; +int gotfds; + +if (cmsg->cmsg_len <CMSG_LEN (sizeof (int )) || + cmsg->cmsg_level !=SOL_SOCKET || + cmsg->cmsg_type !=SCM_RIGHTS ) { +continue ; + } + + fd_size = cmsg->cmsg_len -CMSG_LEN (0 ); + +if (!fd_size) { +continue ; + } + + gotfds = fd_size /sizeof (int ); + *fds =g_renew (int, *fds, *nfds + gotfds); +memcpy (*fds + *nfds,CMSG_DATA (cmsg), fd_size); + +for (i =0 ; i < gotfds; i++) { +int fd = (*fds)[*nfds + i]; +if (fd <0 ) { +continue ; + } + +/* O_NONBLOCK is preserved across SCM_RIGHTS so reset it */ +qemu_set_block (fd); + +#ifndef MSG_CMSG_CLOEXEC +qemu_set_cloexec (fd); +#endif + } + *nfds += gotfds; + } +} + + +static ssize_t qio_channel_socket_readv (QIOChannel *ioc, +const struct iovec *iov, +size_t niov, +int **fds, +size_t *nfds, +int flags, +Error **errp) +{ +QIOChannelSocket *sioc =QIO_CHANNEL_SOCKET (ioc); +ssize_t ret; +struct msghdr msg = {NULL , }; +char control[CMSG_SPACE (sizeof (int ) *SOCKET_MAX_FDS )]; +int sflags =0 ; + +if (flags & ~QIO_CHANNEL_READ_PEEK ) { +error_setg_errno (errp,EINVAL , +_ ("Flags %x are not supported" ), + flags & ~QIO_CHANNEL_READ_PEEK ); +return -1 ; + } + +#ifdef MSG_CMSG_CLOEXEC + sflags |=MSG_CMSG_CLOEXEC ; +#endif +if (flags &QIO_CHANNEL_READ_PEEK ) { + sflags |=MSG_PEEK ; + } + + msg.msg_iov = (struct iovec *)iov; + msg.msg_iovlen = niov; +if (fds && nfds) { + msg.msg_control = control; + msg.msg_controllen =sizeof (control); + } + + retry: + ret =recvmsg (sioc->fd, &msg, sflags); +if (ret <0 ) { +if (socket_error () ==EAGAIN ) { +return -2 ; + } +if (socket_error () ==EINTR ) { + goto retry; + } + +error_setg_errno (errp,socket_error (),"%s" , +_ ("Unable to read from socket" )); +return -1 ; + } + +if (fds && nfds) { +qio_channel_socket_copy_fds (&msg, fds, nfds); + } + +return ret; +} + +static ssize_t qio_channel_socket_writev (QIOChannel *ioc, +const struct iovec *iov, +size_t niov, +int *fds, +size_t nfds, + int flags G_GNUC_UNUSED, +Error **errp) +{ +QIOChannelSocket *sioc =QIO_CHANNEL_SOCKET (ioc); +ssize_t ret; +struct msghdr msg = {NULL , }; + +if (flags) { +error_setg_errno (errp,EINVAL , +_ ("Flags %x are not supported" ), + flags); +return -1 ; + } + + msg.msg_iov = (struct iovec *)iov; + msg.msg_iovlen = niov; + +if (nfds) { +char control[CMSG_SPACE (sizeof (int ) *SOCKET_MAX_FDS )]; +size_t fdsize =sizeof (int ) * nfds; +struct cmsghdr *cmsg; + +if (nfds >SOCKET_MAX_FDS ) { +error_setg_errno (errp, -EINVAL , +_ ("Only %d FDs can be sent, got %zu" ), +SOCKET_MAX_FDS , nfds); +return -1 ; + } + + msg.msg_control = control; + msg.msg_controllen =CMSG_SPACE (sizeof (int ) * nfds); + + cmsg =CMSG_FIRSTHDR (&msg); + cmsg->cmsg_len =CMSG_LEN (fdsize); + cmsg->cmsg_level =SOL_SOCKET ; + cmsg->cmsg_type =SCM_RIGHTS ; +memcpy (CMSG_DATA (cmsg), fds, fdsize); + } + + retry: + ret =sendmsg (sioc->fd, &msg,0 ); +if (ret <=0 ) { +if (socket_error () ==EAGAIN ) { +return -2 ; + } +if (socket_error () ==EINTR ) { + goto retry; + } +error_setg_errno (errp,socket_error (),"%s" , +_ ("Unable to write to socket" )); +return -1 ; + } +return ret; +} +#else /* WIN32 */ +static ssize_t qio_channel_socket_readv (QIOChannel *ioc, +const struct iovec *iov, +size_t niov, +int **fds, +size_t *nfds, +int flags, +Error **errp) +{ +QIOChannelSocket *sioc =QIO_CHANNEL_SOCKET (ioc); +ssize_t done =0 ; +ssize_t i; + +if (flags) { +error_setg_errno (errp,EINVAL , +_ ("Flags %x are not supported" ), + flags); +return -1 ; + } +if (fds || nfds) { +error_setg_errno (errp,EINVAL ,"%s" , +_ ("Channel does not support file descriptor passing" )); +return -1 ; + } + +for (i =0 ; i < niov; i++) { +ssize_t ret; + retry: + ret =recv (sioc->fd, + iov[i].iov_base, + iov[i].iov_len, +0 ); +if (ret <0 ) { +if (socket_error () ==EAGAIN ) { +if (done) { +return done; + }else { +return QIO_CHANNEL_ERR_BLOCK ; + } + }else if (socket_error () ==EINTR ) { + goto retry; + }else { +error_setg_errno (errp,socket_error (),"%s" , +_ ("Unable to write to socket" )); +return -1 ; + } + } + done += ret; +if (ret < iov[i].iov_len) { +return done; + } + } + +return done; +} + +static ssize_t qio_channel_socket_writev (QIOChannel *ioc, +const struct iovec *iov, +size_t niov, +int *fds, +size_t nfds, + int flags G_GNUC_UNUSED, +Error **errp) +{ +QIOChannelSocket *sioc =QIO_CHANNEL_SOCKET (ioc); +ssize_t done =0 ; +ssize_t i; + +if (flags) { +error_setg_errno (errp,EINVAL , +_ ("Flags %x are not supported" ), + flags); +return -1 ; + } +if (fds || nfds) { +error_setg_errno (errp,EINVAL ,"%s" , +_ ("Channel does not support file descriptor passing" )); +return -1 ; + } + +for (i =0 ; i < niov; i++) { +ssize_t ret; + retry: + ret =send (sioc->fd, + iov[i].iov_base, + iov[i].iov_len, +0 ); +if (ret <0 ) { +if (socket_error () ==EAGAIN ) { +if (done) { +return done; + }else { +return QIO_CHANNEL_ERR_BLOCK ; + } + }else if (socket_error () ==EINTR ) { + goto retry; + }else { +error_setg_errno (errp,socket_error (),"%s" , +_ ("Unable to write to socket" )); +return -1 ; + } + } + done += ret; +if (ret < iov[i].iov_len) { +return done; + } + } + +return done; +} +#endif /* WIN32 */ + +static void qio_channel_socket_set_blocking (QIOChannel *ioc, +bool enabled) +{ +QIOChannelSocket *sioc =QIO_CHANNEL_SOCKET (ioc); + +if (enabled) { +qemu_set_block (sioc->fd); + }else { +qemu_set_nonblock (sioc->fd); + } +} + +void +qio_channel_socket_set_nodelay (QIOChannelSocket *ioc, +bool enabled) +{ +QIOChannelSocket *sioc =QIO_CHANNEL_SOCKET (ioc); +int v = enabled ?1 :0 ; + +qemu_setsockopt (sioc->fd,IPPROTO_TCP ,TCP_NODELAY , &v,sizeof (v)); +} + +static int qio_channel_socket_close (QIOChannel *ioc, +Error **errp) +{ +QIOChannelSocket *sioc =QIO_CHANNEL_SOCKET (ioc); + +if (closesocket (sioc->fd) <0 ) { + sioc->fd =-1 ; +error_setg_errno (errp,socket_error (),"%s" , +_ ("Unable to close socket" )); +return -1 ; + } + sioc->fd =-1 ; +return 0 ; +} + +int +qio_channel_socket_shutdown (QIOChannelSocket *ioc, +QIOChannelSocketShutdown how, +Error **errp) +{ +int sockhow; +switch (how) { +case QIO_CHANNEL_SOCKET_SHUTDOWN_READ : + sockhow =SHUT_RD ; +break ; +case QIO_CHANNEL_SOCKET_SHUTDOWN_WRITE : + sockhow =SHUT_WR ; +break ; +case QIO_CHANNEL_SOCKET_SHUTDOWN_BOTH : +default : + sockhow =SHUT_RDWR ; +break ; + } + +if (shutdown (ioc->fd, sockhow) <0 ) { +error_setg_errno (errp,socket_error (),"%s" , +_ ("Unable to shutdown socket" )); +return -1 ; + } +return 0 ; +} + +static GSource *qio_channel_socket_create_watch (QIOChannel *ioc, +GIOCondition condition) +{ +QIOChannelSocket *sioc =QIO_CHANNEL_SOCKET (ioc); +return qio_channel_unix_create_fd_watch (ioc, + sioc->fd, + condition); +} + +static void qio_channel_socket_class_init (ObjectClass *klass, + void *class_data G_GNUC_UNUSED) +{ +QIOChannelClass *ioc_klass =QIO_CHANNEL_CLASS (klass); + + ioc_klass->io_has_feature = qio_channel_socket_has_feature; + ioc_klass->io_writev = qio_channel_socket_writev; + ioc_klass->io_readv = qio_channel_socket_readv; + ioc_klass->io_set_blocking = qio_channel_socket_set_blocking; + ioc_klass->io_close = qio_channel_socket_close; + ioc_klass->io_create_watch = qio_channel_socket_create_watch; +} + +static const TypeInfo qio_channel_socket_info = { + .parent =TYPE_QIO_CHANNEL , + .name =TYPE_QIO_CHANNEL_SOCKET , + .instance_size =sizeof (QIOChannelSocket), + .instance_init = qio_channel_socket_init, + .instance_finalize = qio_channel_socket_finalize, + .class_init = qio_channel_socket_class_init, +}; + +static void qio_channel_socket_register_types (void ) +{ +type_register_static (&qio_channel_socket_info); +} + +type_init (qio_channel_socket_register_types);
Re: [Qemu-devel] [PATCH v1 RFC 23/34] io: add QIOChannelSocket class
Quoted 64 lines · Paolo Bonzini
Quoted 61 lines · Daniel P. Berrange
Implement a QIOChannel subclass that supports sockets I/O
TBD check errno handling of windows port & fix watch impl
Signed-off-by Daniel P. Berrange <[email protected]>--- include/io/channel-socket.h | 168 +++++++++++++ io/Makefile.objs | 1 + io/channel-socket.c | 572 ++++++++++++++++++++++++++++++++++++++++++++ 3 files changed, 741 insertions(+) create mode 100644 include/io/channel-socket.h create mode 100644 io/channel-socket.cinclude/io/channel-socket.h new file +41
-0+/** +* qio_channel_socket_get_local_addr_string: +* @ioc: the socket channel object +* @hostname: pointer to be filled with hostname string +* @servicename: pointer to be filled with servicename string +* @family: pointer to be filled with network address family +* @errp: pointer to an uninitialized error object +* +* Get the string representation of the local socket +* address. The address information will be stored in +* the @hostname, @servicename and @family parameters. +* The @hostname and @servicename strings will be +* allocated to the size required and should be free +* with g_free() when no longer required +* +* Returns: 0 on success, -1 on error +*/ +int +qio_channel_socket_get_local_addr_string (QIOChannelSocket *ioc, +char **hostname, +char **servicename, +NetworkAddressFamily *family, +Error **errp); + +/** +* qio_channel_socket_get_remote_addr_string: +* @ioc: the socket channel object +* @hostname: pointer to be filled with hostname string +* @servicename: pointer to be filled with servicename string +* @family: pointer to be filled with network address family +* @errp: pointer to an uninitialized error object +* +* Get the string representation of the remote socket +* address. The address information will be stored in +* the @hostname, @servicename and @family parameters. +* The @hostname and @servicename strings will be +* allocated to the size required and should be free +* with g_free() when no longer required +* +* Returns: 0 on success, -1 on error +*/
Quoted 8 lines
include/io/channel-socket.h line 113
Quoted 6 lines
include/io/channel-socket.h new file +6
-0+int +qio_channel_socket_get_remote_addr_string (QIOChannelSocket *ioc, +char **hostname, +char **servicename, +NetworkAddressFamily *family, +Error **errp);Would it be possible to change these to use a SocketAddress* type?
Yeah, that looks like it is a viable option.
Quoted 11 lines
include/io/channel-socket.h line 120
Quoted 7 lines
include/io/channel-socket.h new file +7
-0+ +/** +* qio_channel_socket_set_nodelay: +* @ioc: the socket channel object +* @enabled: the new flag state +* +* Set the state of the NODELAY socket flag. If the The function name is okay, but please write
TCP_NODELAYin the documentation, or talk about Nagle's algorithm instead of mentioning the flag.
Sure will do.
Quoted 21 lines
Quoted 21 lines
+ * @enabled parameter is true, then NODELAY will be
+ * set and data will be transmitted immediately. If
+ * @enabled is false, then data may be temporarily
+ * held for transmission to enable writes to be
+ * coallesced.
+ */include/io/channel-socket.h new file +15
-0+void +qio_channel_socket_set_nodelay (QIOChannelSocket *ioc, +bool enabled); + +/** +* qio_channel_socket_accept: +* @ioc: the socket channel object +* @errp: pointer to an uninitialized error object +* +* If the socket represents a server, then this accepts +* a new client connection. The returned channel will +* represent the connected client socket. +* +* Returns: the new client channel, or NULL on error +*/
Quoted 8 lines
include/io/channel-socket.h line 144
Quoted 3 lines
include/io/channel-socket.h new file +3
-0+QIOChannelSocket * +qio_channel_socket_accept (QIOChannelSocket *ioc, +Error **errp);Does it make sense for a passive socket to be a QIOChannelSocket? We have already a pretty decent API in
util/qemu-sockets.c, and QIOChannelSocket will become more similar to qemu-sockets if you switch to SocketAddress. Perhaps this function can just take a file descriptor?
I was somewhat undecided about that really - One of my todos is to see about better integrating with qemu-sockets for the connection facilities, so will consider this problem too.
Regards,
Daniel
Signature · 4 lines
|: http://berrange.com -o- http://www.flickr.com/photos/dberrange/ :| |: http://libvirt.org -o- http://virt-manager.org :| |: http://autobuild.org -o- http://search.cpan.org/~danberr/ :| |: http://entangle-photo.org -o- http://live.gnome.org/gtk-vnc :|
Re: [Qemu-devel] [PATCH v1 RFC 23/34] io: add QIOChannelSocket class
Quoted 11 lines · Daniel P. Berrange
Quoted 8 lines
Quoted 8 lines
Quoted 3 lines
include/io/channel-socket.h line 144
Quoted 3 lines
include/io/channel-socket.h new file +3
-0+QIOChannelSocket * +qio_channel_socket_accept (QIOChannelSocket *ioc, +Error **errp);Does it make sense for a passive socket to be a QIOChannelSocket? We have already a pretty decent API in
util/qemu-sockets.c, and QIOChannelSocket will become more similar to qemu-sockets if you switch to SocketAddress. Perhaps this function can just take a file descriptor?I was somewhat undecided about that really - One of my todos is to see about better integrating with qemu-sockets for the connection facilities, so will consider this problem too.
Hmm, I guess it makes sense to have the passive socket as a QOM object, so it is okay.
Paolo
Re: [Qemu-devel] [PATCH v1 RFC 23/34] io: add QIOChannelSocket class
In reply to Daniel P. Berrangeinclude/io/channel-socket.h line 50
Quoted 12 lines · Daniel P. Berrange
Implement a QIOChannel subclass that supports sockets I/O
TBD check errno handling of windows port & fix watch impl
include/io/channel-socket.h new file +8
-0+struct QIOChannelSocket { +QIOChannel parent; +int fd; +struct sockaddr_storage localAddr; +socklen_t localAddrLen; +struct sockaddr_storage remoteAddr; +socklen_t remoteAddrLen; +};
Looks like this supports a single listening socket only, correct? That is a long-standing issue we have in qemu. Listening on both ipv4 and ipv6 doesn't work, except when binding the socket to the ipv6 wildcard address which can accept ipv4 connects too on most systems.
Would be nice to tackle that while putting the socket code upside down anyway.
cheers,
Gerd
Re: [Qemu-devel] [PATCH v1 RFC 23/34] io: add QIOChannelSocket class
Quoted 19 lines · Gerd Hoffmann
include/io/channel-socket.h line 50
Quoted 12 lines · Daniel P. Berrange
Implement a QIOChannel subclass that supports sockets I/O
TBD check errno handling of windows port & fix watch impl
include/io/channel-socket.h new file +8
-0+struct QIOChannelSocket { +QIOChannel parent; +int fd; +struct sockaddr_storage localAddr; +socklen_t localAddrLen; +struct sockaddr_storage remoteAddr; +socklen_t remoteAddrLen; +};Looks like this supports a single listening socket only, correct? That is a long-standing issue we have in qemu. Listening on both ipv4 and ipv6 doesn't work, except when binding the socket to the ipv6 wildcard address which can accept ipv4 connects too on most systems.
Quoted 2 lines
Would be nice to tackle that while putting the socket code upside down anyway.
Thanks for reminding me about this limitation - I'll have a think about how we can solve this problem at the same time too.
Regards,
Daniel
Signature · 4 lines
|: http://berrange.com -o- http://www.flickr.com/photos/dberrange/ :| |: http://libvirt.org -o- http://virt-manager.org :| |: http://autobuild.org -o- http://search.cpan.org/~danberr/ :| |: http://entangle-photo.org -o- http://live.gnome.org/gtk-vnc :|
RFC PATCH 24/34 io: add QIOChannelFile class
In reply to Daniel P. BerrangeAdd a QIOChannel subclass that is capable of operating on things that are files, such as plain files, pipes, character/block devices, but notably not sockets.
--- include/io/channel-file.h | 67 ++++++++++++++++ io/Makefile.objs | 1 + io/channel-file.c | 198 ++++++++++++++++++++++++++++++++++++++++++++++ 3 files changed, 266 insertions(+) create mode 100644 include/io/channel-file.h create mode 100644 io/channel-file.c
include/io/channel-file.h new file +67 -0
@@ -0,0 +1,67 @@ +/* +* QEMU I/O channels files driver +* +* Copyright (c) 2015 Red Hat, Inc. +* +* This library is free software; you can redistribute it and/or +* modify it under the terms of the GNU Lesser General Public +* License as published by the Free Software Foundation; either +* version 2 of the License, or (at your option) any later version. +* +* This library is distributed in the hope that it will be useful, +* but WITHOUT ANY WARRANTY; without even the implied warranty of +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +* Lesser General Public License for more details. +* +* You should have received a copy of the GNU Lesser General Public +* License along with this library; if not, see <http://www.gnu.org/licenses/>. +* +*/ + +#ifndef QIO_CHANNEL_FILE_H__ +#define QIO_CHANNEL_FILE_H__ + +#include "io/channel.h" + +#define TYPE_QIO_CHANNEL_FILE "qemu:io-channel-file" +#define QIO_CHANNEL_FILE (obj) \ + OBJECT_CHECK(QIOChannelFile, (obj), TYPE_QIO_CHANNEL_FILE) + +typedef struct _QIOChannelFile QIOChannelFile ; + +/** +* QIOChannelFile: +* +* The QIOChannelFile object provides a channel implementation +* that is able to perform I/O on block devices, character +* devices, FIFOs, pipes and plain files. While it is technically +* able to work on sockets too on the UNIX platform, this is not +* portable to Windows and lacks some extra sockets specific +* functionality. So the QIOChannelSocket object is recommended +* for that use case. +* +*/ + +struct _QIOChannelFile { +QIOChannel parent; +int fd; +}; + + +/** +* qio_channel_file_new_fd: +* @fd: the file descriptor +* +* Create a new IO channel object for a file represented +* by the @fd parameter. @fd can be associated with a +* block device, character device, fifo, pipe, or a +* regular file. For sockets, the QIOChannelSocket class +* should be used instead, as this provides greater +* functionality and cross platform portability. +* +* Returns: the new channel object +*/ +QIOChannelFile * +qio_channel_file_new_fd (int fd); + +#endif /* QIO_CHANNEL_FILE_H__ */
io/Makefile.objs +1 -0
@@ -1,3 +1,4 @@ util-obj-y += channel.o util-obj-y += channel-unix.o util-obj-y += channel-socket.o +util-obj-y += channel-file.o
io/channel-file.c new file +198 -0
198 lines not on this page · raw message
Signature · 1 line
2.1.0
RFC PATCH 25/34 io: add QIOTask class for async operations
In reply to Daniel P. BerrangeA number of I/O operations need to be performed asynchronously to avoid blocking the main loop. The caller of such APIs need to provide a callback to be invoked on completion/error and need access to the error, if any. The small QIOTask provides a simple framework for dealing with such probes. The API docs inline provide an outline of how this is to be used.
In this series, the QIOTask class will be used for things like the TLS handshake, the websockets handshake and TCP connect() progress.
--- include/io/task.h | 168 ++++++++++++++++++++++++++++++++++++++++++++++++++++++ io/Makefile.objs | 1 + io/task.c | 84 +++++++++++++++++++++++++++ 3 files changed, 253 insertions(+) create mode 100644 include/io/task.h create mode 100644 io/task.c
include/io/task.h new file +168 -0
@@ -0,0 +1,168 @@ +/* +* QEMU I/O task +* +* Copyright (c) 2015 Red Hat, Inc. +* +* This library is free software; you can redistribute it and/or +* modify it under the terms of the GNU Lesser General Public +* License as published by the Free Software Foundation; either +* version 2 of the License, or (at your option) any later version. +* +* This library is distributed in the hope that it will be useful, +* but WITHOUT ANY WARRANTY; without even the implied warranty of +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +* Lesser General Public License for more details. +* +* You should have received a copy of the GNU Lesser General Public +* License along with this library; if not, see <http://www.gnu.org/licenses/>. +* +*/ + +#ifndef QIO_TASK_H__ +#define QIO_TASK_H__ + +#include "qemu-common.h" +#include "qapi/error.h" +#include "qom/object.h" + +#define TYPE_QIO_TASK "qemu:io-task" +#define QIO_TASK (obj) \ + OBJECT_CHECK(QIOTask, (obj), TYPE_QIO_TASK) +#define QIO_TASK_CLASS (klass) \ + OBJECT_CLASS_CHECK(QIOTaskClass, klass, TYPE_QIO_TASK) +#define QIO_TASK_GET_CLASS (obj) \ + OBJECT_GET_CLASS(QIOTaskClass, obj, TYPE_QIO_TASK) + +typedef struct QIOTask QIOTask ; +typedef struct QIOTaskClass QIOTaskClass ; + +typedef void (*QIOTaskFunc )(QIOTask *task, +gpointer opaque); + +/** +* QIOTask: +* +* The QIOTask object provides a simple mechanism for reporting +* success / failure of long running background operations. +* +* A object on which the operation is to be performed could have +* a public API which accepts a task callback: +* +* void myobject_operation(QMyObject *obj, +* QIOTaskFunc *func, +* gpointer opaque, +* GDestroyNotify *notify); +* +* The 'func' parameter is the callback to be invoked, and 'opaque' +* is data to pass to it. The optional 'notify' function is used +* to free 'opaque' when no longer needed. +* +* Now, lets say the implementation of this method wants to set +* a timer to run once a second checking for completion of some +* activity. It would do something like +* +* void myobject_operation(QMyObject *obj, +* QIOTaskFunc *func, +* gpointer opaque, +* GDestroyNotify *notify) +* { +* QIOTask *task; +* +* task = qio_task_new(OBJECT(obj), func, opaque, notify); +* +* g_timeout_add_full(G_PRIORITY_DEFAULT, +* 1000, +* myobject_operation_timer, +* task, +* (GDestroyNotify)object_unref); +* } +* +* It could equally have setup a watch on a file descriptor or +* created a background thread, or something else entirely. +* Notice that the source object is passed to the task, and +* QIOTask will hold a reference on that. This ensure that +* the QMyObject instance cannot be garbage collected while +* the async task is still in progress. +* +* In this case, myobject_operation_timer will fire after +* 3 secs and do +* +* gboolean myobject_operation_timer(gpointer opaque) +* { +* QIOTask *task = QIO_TASK(opaque); +* Error *err;* +* +* ...check something important... +* if (err) { +* qio_task_abort(task, err); +* error_free(task); +* return FALSE; +* } else if (...work is completed ...) { +* qio_task_complete(task); +* return FALSE; +* } +* ...carry on polling ... +* return TRUE; +* } +* +* Once this function returns false, object_unref will be called +* automatically on the task causing it to be released and the +* ref on QMyObject dropped too. +*/ + +struct QIOTask { +Object parent; +Object *source; +QIOTaskFunc func; +gpointer opaque; +GDestroyNotify destroy; +Error *err; +}; + +struct QIOTaskClass { +ObjectClass parent; +}; + +/** +* qio_task_new: +* @source: the object on which the operation is invoked +* @func: the callback to invoke when the task completes +* @opaque: opaque data to pass to @func when invoked +* @destroy: optional callback to free @opaque +* +* Creates a new task object to track completion of a +* background operation running on the object @source. +* When the operation completes or fails, the callback +* @func will be invoked. The callback can access the +* 'err' attribute in the task object to determine if +* the operation was successful or not. +* +* Returns: the task object +*/ +QIOTask *qio_task_new (Object *source, +QIOTaskFunc func, +gpointer opaque, +GDestroyNotify destroy); + +/** +* qio_task_complete: +* @task: the task object +* +* Mark the operation as succesfully completed +*/ +void qio_task_complete (QIOTask *task); + +/** +* qio_task_abort: +* @task: the task object +* @err: the error to record for the operation +* +* Mark the operation as failed, with @err providing +* details about the failure. The @err may be freed +* afer the function returns, as the notification +* callback is invoked synchronously. +*/ +void qio_task_abort (QIOTask *task, +Error *err); + +#endif /* QIO_TASK_H__ */
io/Makefile.objs +1 -0
@@ -1,3 +1,4 @@ +util-obj-y += task.o util-obj-y += channel.o util-obj-y += channel-unix.o util-obj-y += channel-socket.o
io/task.c new file +84 -0
84 lines not on this page · raw message
Signature · 1 line
2.1.0
Re: [Qemu-devel] [PATCH v1 RFC 25/34] io: add QIOTask class for async operations
Quoted 10 lines · Daniel P. Berrange
A number of I/O operations need to be performed asynchronously to avoid blocking the main loop. The caller of such APIs need to provide a callback to be invoked on completion/error and need access to the error, if any. The small QIOTask provides a simple framework for dealing with such probes. The API docs inline provide an outline of how this is to be used.
In this series, the QIOTask class will be used for things like the TLS handshake, the websockets handshake and TCP
connect()progress.
Is this actually worth making it a heavyweight QOM object? In general if you don't do object_property_add_child (and I don't think here you do), it's simpler to just make it a C struct.
In this case I even think you're leaking the task. You do object_ref twice (once at creation time, once before qio_channel_add_watch_full) and only have one object_unref. I would just do without reference counting, and add a qio_task_free() function that calls qio_task_finalize() and frees the QIOTask.
BTW, do you have plans to use the GDestroyNotify argument to qio_task_new, or is it just for consistency?
Paolo
Re: [Qemu-devel] [PATCH v1 RFC 25/34] io: add QIOTask class for async operations
Quoted 10 lines · Paolo Bonzini
Quoted 7 lines · Daniel P. Berrange
A number of I/O operations need to be performed asynchronously to avoid blocking the main loop. The caller of such APIs need to provide a callback to be invoked on completion/error and need access to the error, if any. The small QIOTask provides a simple framework for dealing with such probes. The API docs inline provide an outline of how this is to be used.
Quoted 7 lines
Quoted 3 lines
In this series, the QIOTask class will be used for things like the TLS handshake, the websockets handshake and TCP
connect()progress.Is this actually worth making it a heavyweight QOM object? In general if you don't do
object_property_add_child(and I don't think here you do), it's simpler to just make it a C struct.
Essentially I just used QOM anywhere that I wanted to have ref counting, even if I didn't need the property feature. I figure this particular usage isn't performance critical so using the more heavyweight QOM framework wasn't the end of the world.
Quoted 5 lines
In this case I even think you're leaking the task. You do
object_reftwice (once at creation time, once beforeqio_channel_add_watch_full) and only have oneobject_unref. I would just do without reference counting, and add aqio_task_free()function that callsqio_task_finalize()and frees the QIOTask.
Are you referring to the qio_channel_tls_handshake() method in the next patch ? If so it does actually have two object_unref calls so shouldn't be leaking. In more complex scenarios I thnk the ref counting ability will come in useful. Of course I could add ref counting to a plain struct without using QOM, but it felt better to just use QOM and be done with it, so people don't have to remember which particular unref method they must use.
Quoted 2 lines
BTW, do you have plans to use the GDestroyNotify argument to
qio_task_new, or is it just for consistency?
I'm not 100% sure if I'll need it or not yet. One of my todo items is to double check the sequence of operations when a VNC/chardev client disconnects while a background task is in progress. It is possible I might need to hold a reference on the VNC/chardev state in which case the GDestroyNotify arg will come in useful. So I just added it for consistency initially.
Regards,
Daniel
Signature · 4 lines
|: http://berrange.com -o- http://www.flickr.com/photos/dberrange/ :| |: http://libvirt.org -o- http://virt-manager.org :| |: http://autobuild.org -o- http://search.cpan.org/~danberr/ :| |: http://entangle-photo.org -o- http://live.gnome.org/gtk-vnc :|
Re: [Qemu-devel] [PATCH v1 RFC 25/34] io: add QIOTask class for async operations
Quoted 6 lines · Daniel P. Berrange
Quoted 5 lines
In this case I even think you're leaking the task. You do
object_reftwice (once at creation time, once beforeqio_channel_add_watch_full) and only have oneobject_unref. I would just do without reference counting, and add aqio_task_free()function that callsqio_task_finalize()and frees the QIOTask.
Quoted 7 lines
Are you referring to the
qio_channel_tls_handshake()method in the next patch ? If so it does actually have twoobject_unrefcalls so shouldn't be leaking. In more complex scenarios I thnk the ref counting ability will come in useful. Of course I could add ref counting to a plain struct without using QOM, but it felt better to just use QOM and be done with it, so people don't have to remember which particular unref method they must use.
I cannot find the second... O:-)
Quoted 9 lines
Quoted 2 lines
BTW, do you have plans to use the GDestroyNotify argument to
qio_task_new, or is it just for consistency?I'm not 100% sure if I'll need it or not yet. One of my todo items is to double check the sequence of operations when a VNC/chardev client disconnects while a background task is in progress. It is possible I might need to hold a reference on the VNC/chardev state in which case the GDestroyNotify arg will come in useful. So I just added it for consistency initially.
Yup, no problem there.
Paolo
Re: [Qemu-devel] [PATCH v1 RFC 25/34] io: add QIOTask class for async operations
Quoted 17 lines · Paolo Bonzini
Quoted 6 lines · Daniel P. Berrange
Quoted 5 lines
In this case I even think you're leaking the task. You do
object_reftwice (once at creation time, once beforeqio_channel_add_watch_full) and only have oneobject_unref. I would just do without reference counting, and add aqio_task_free()function that callsqio_task_finalize()and frees the QIOTask.Quoted 7 lines
Are you referring to the
qio_channel_tls_handshake()method in the next patch ? If so it does actually have twoobject_unrefcalls so shouldn't be leaking. In more complex scenarios I thnk the ref counting ability will come in useful. Of course I could add ref counting to a plain struct without using QOM, but it felt better to just use QOM and be done with it, so people don't have to remember which particular unref method they must use.
Quoted 1 line
I cannot find the second... O:-)
I create & unref it in the same place:
+ task = qio_task_new(OBJECT(ioc),
+ func, opaque, destroy);
+
+ qio_channel_tls_handshake_task(ioc, task);
+
+ object_unref(OBJECT(task));
The second ref taken at time of qio_channel_add_watch_full() gets released by the GDestroyNotify that is passed to that method.
Regards,
Daniel
Signature · 4 lines
|: http://berrange.com -o- http://www.flickr.com/photos/dberrange/ :| |: http://libvirt.org -o- http://virt-manager.org :| |: http://autobuild.org -o- http://search.cpan.org/~danberr/ :| |: http://entangle-photo.org -o- http://live.gnome.org/gtk-vnc :|
Re: [Qemu-devel] [PATCH v1 RFC 25/34] io: add QIOTask class for async operations
Quoted 10 lines · Daniel P. Berrange
(quoted diff) +6
-0+ task = qio_task_new(OBJECT(ioc), + func, opaque, destroy); + + qio_channel_tls_handshake_task(ioc, task); + + object_unref(OBJECT(task));The second ref taken at time of
qio_channel_add_watch_full()gets released by the GDestroyNotify that is passed to that method.
/me is blind.
So this means you really do not need the reference counting; qio_channel_tls_handshake_task (which is static anyway) is what manages the lifetime of the QIOTask, it can take ownership of the task right after it is created.
In effect QIOTask is just wrapping the (func, opaque, destroy) continuation. It makes sense that it doesn't need reference counting, because it has a well-defined point of death (just before the continuation is called, or just before you find out it will never be called). So I think it's okay to remove the reference counting and make it a simple heap-allocated struct. gio_channel_tls_handshake_task can free it after calling gio_task_{abort,complete}.
Without the reference counting the GDestroyNotify also becomes unnecessary (and the fact that you're using QIOTask as a simple continuation explains why you didn't need GDestroyNotify), but it's okay if you want to leave it in.
I would also have QIOTaskFunc take the "exploded" struct, i.e. typedef void QIOTaskFunc(QMyObject *, gpointer, Error *), and hide QIOTask entirely from the user.
Paolo
Re: [Qemu-devel] [PATCH v1 RFC 25/34] io: add QIOTask class for async operations
Quoted 29 lines · Paolo Bonzini
Quoted 10 lines · Daniel P. Berrange
(quoted diff) +6
-0+ task = qio_task_new(OBJECT(ioc), + func, opaque, destroy); + + qio_channel_tls_handshake_task(ioc, task); + + object_unref(OBJECT(task));The second ref taken at time of
qio_channel_add_watch_full()gets released by the GDestroyNotify that is passed to that method./me is blind.
So this means you really do not need the reference counting;
qio_channel_tls_handshake_task(which is static anyway) is what manages the lifetime of the QIOTask, it can take ownership of the task right after it is created.In effect QIOTask is just wrapping the (func, opaque, destroy) continuation. It makes sense that it doesn't need reference counting, because it has a well-defined point of death (just before the continuation is called, or just before you find out it will never be called). So I think it's okay to remove the reference counting and make it a simple heap-allocated struct.
gio_channel_tls_handshake_taskcan free it after calling gio_task_{abort,complete}.
Quoted 8 lines
Without the reference counting the GDestroyNotify also becomes unnecessary (and the fact that you're using QIOTask as a simple continuation explains why you didn't need GDestroyNotify), but it's okay if you want to leave it in.
I would also have QIOTaskFunc take the "exploded" struct, i.e. typedef void
QIOTaskFunc(QMyObject *, gpointer, Error *), and hide QIOTask entirely from the user.
Yeah, I can try that approach and see how it works out.
Regards,
Daniel
Signature · 4 lines
|: http://berrange.com -o- http://www.flickr.com/photos/dberrange/ :| |: http://libvirt.org -o- http://virt-manager.org :| |: http://autobuild.org -o- http://search.cpan.org/~danberr/ :| |: http://entangle-photo.org -o- http://live.gnome.org/gtk-vnc :|
RFC PATCH 26/34 io: add QIOChannelTLS class
In reply to Daniel P. BerrangeAdd a QIOChannel subclass that can run the TLS protocol over the top of another QIOChannel instance.
--- include/io/channel-tls.h | 142 +++++++++++++++++ io/Makefile.objs | 1 + io/channel-tls.c | 393 +++++++++++++++++++++++++++++++++++++++++++++++ 3 files changed, 536 insertions(+) create mode 100644 include/io/channel-tls.h create mode 100644 io/channel-tls.c
include/io/channel-tls.h new file +142 -0
@@ -0,0 +1,142 @@ +/* +* QEMU I/O channels TLS driver +* +* Copyright (c) 2015 Red Hat, Inc. +* +* This library is free software; you can redistribute it and/or +* modify it under the terms of the GNU Lesser General Public +* License as published by the Free Software Foundation; either +* version 2 of the License, or (at your option) any later version. +* +* This library is distributed in the hope that it will be useful, +* but WITHOUT ANY WARRANTY; without even the implied warranty of +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +* Lesser General Public License for more details. +* +* You should have received a copy of the GNU Lesser General Public +* License along with this library; if not, see <http://www.gnu.org/licenses/>. +* +*/ + +#ifndef QIO_CHANNEL_TLS_H__ +#define QIO_CHANNEL_TLS_H__ + +#include "io/channel.h" +#include "io/task.h" +#include "crypto/tlssession.h" + +#define TYPE_QIO_CHANNEL_TLS "qemu:io-channel-tls" +#define QIO_CHANNEL_TLS (obj) \ + OBJECT_CHECK(QIOChannelTLS, (obj), TYPE_QIO_CHANNEL_TLS) + +typedef struct QIOChannelTLS QIOChannelTLS ; + +/** +* QIOChannelTLS +* +* The QIOChannelTLS class provides a channel wrapper which +* can transparently run the TLS encryption protocol. It is +* usually used over a TCP socket, but there is actually no +* technical restriction on which type of master channel is +* used as the transport. +* +* This channel object is capable of running as either a +* TLS server or TLS client. +*/ + +struct QIOChannelTLS { +QIOChannel parent; +QIOChannel *master; +QCryptoTLSSession *session; +}; + +/** +* qio_channel_tls_new_server: +* @master: the underlying channel object +* @creds: the credentials to use for TLS handshake +* @aclname: the access control list for validating clients +* @errp: pointer to an uninitialized error object +* +* Create a new TLS channel that runs the server side of +* a TLS session. The TLS session handshake will use the +* credentials provided in @creds. If the @aclname parameter +* is non-NULL, then the client will have to provide +* credentials (ie a x509 client certificate) which will +* then be validated against the ACL. +* +* After creating the channel, it is mandatory to call +* the qio_channel_tls_handshake() method before attempting +* todo any I/O on the channel. +* +* Once the handshake has completed, all I/O should be done +* via the new TLS channel object and not the original +* master channel +* +* Returns: the new TLS channel object, or NULL +*/ +QIOChannelTLS * +qio_channel_tls_new_server (QIOChannel *master, +QCryptoTLSCreds *creds, +const char *aclname, +Error **errp); + +/** +* qio_channel_tls_new_client: +* @master: the underlying channel object +* @creds: the credentials to use for TLS handshake +* @hostname: the user specified server hostname +* @errp: pointer to an uninitialized error object +* +* Create a new TLS channel that runs the client side of +* a TLS session. The TLS session handshake will use the +* credentials provided in @creds. The @hostname parameter +* should provide the user specified hostname of the server +* and will be validated against the server's credentials +* (ie CommonName of the x509 certificate) +* +* After creating the channel, it is mandatory to call +* the qio_channel_tls_handshake() method before attempting +* todo any I/O on the channel. +* +* Once the handshake has completed, all I/O should be done +* via the new TLS channel object and not the original +* master channel +* +* Returns: the new TLS channel object, or NULL +*/ +QIOChannelTLS * +qio_channel_tls_new_client (QIOChannel *master, +QCryptoTLSCreds *creds, +const char *hostname, +Error **errp); + +/** +* qio_channel_tls_handshake: +* @ioc: the TLS channel object +* @func: the callback to invoke when completed +* @opaque: opaque data to pass to @func +* @destroy: optional callback to free @opaque +* +* Perform the TLS session handshake. This method +* will return immediately and the handshake will +* continue in the background, provided the main +* loop is running. When the handshake is complete, +* or fails, the @func callback will be invoked. +*/ +void qio_channel_tls_handshake (QIOChannelTLS *ioc, +QIOTaskFunc func, +gpointer opaque, +GDestroyNotify destroy); + +/** +* qio_channel_tls_get_session: +* @ioc: the TLS channel object +* +* Get the TLS session used by the channel. +* +* Returns: the TLS session +*/ +QCryptoTLSSession * +qio_channel_tls_get_session (QIOChannelTLS *ioc); + +#endif /* QIO_CHANNEL_TLS_H__ */
io/Makefile.objs +1 -0
@@ -3,3 +3,4 @@ util-obj-y += channel.o util-obj-y += channel-unix.o util-obj-y += channel-socket.o util-obj-y += channel-file.o +util-obj-y += channel-tls.o
io/channel-tls.c new file +393 -0
393 lines not on this page · raw message
Signature · 1 line
2.1.0
RFC PATCH 27/34 io: pull Buffer code out of VNC module
In reply to Daniel P. BerrangeThe Buffer code in the VNC server is useful for the IO channel code, so pull it out into a shared module, QIOBuffer.
--- include/io/buffer.h | 118 ++++++++++++++++++++++++++++++++++++++++++++++++++++ io/Makefile.objs | 1 + io/buffer.c | 65 +++++++++++++++++++++++++++++ ui/vnc-auth-sasl.c | 4 +- ui/vnc-enc-tight.c | 38 ++++++++--------- ui/vnc-enc-zlib.c | 6 +-- ui/vnc-enc-zrle.c | 18 ++++---- ui/vnc-jobs.c | 15 +++---- ui/vnc-ws.c | 36 ++++++++-------- ui/vnc-ws.h | 6 +-- ui/vnc.c | 67 ++++++----------------------- ui/vnc.h | 50 ++++++++-------------- 12 files changed, 276 insertions(+), 148 deletions(-) create mode 100644 include/io/buffer.h create mode 100644 io/buffer.c
include/io/buffer.h new file +118 -0
@@ -0,0 +1,118 @@ +/* +* QEMU I/O buffers +* +* Copyright (c) 2015 Red Hat, Inc. +* +* This library is free software; you can redistribute it and/or +* modify it under the terms of the GNU Lesser General Public +* License as published by the Free Software Foundation; either +* version 2 of the License, or (at your option) any later version. +* +* This library is distributed in the hope that it will be useful, +* but WITHOUT ANY WARRANTY; without even the implied warranty of +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +* Lesser General Public License for more details. +* +* You should have received a copy of the GNU Lesser General Public +* License along with this library; if not, see <http://www.gnu.org/licenses/>. +* +*/ + +#ifndef QIO_BUFFER_H__ +#define QIO_BUFFER_H__ + +#include "qemu-common.h" + +typedef struct QIOBuffer QIOBuffer ; + +/** +* QIOBuffer: +* +* The QIOBuffer object provides a simple dynamically resizing +* array, with separate tracking of capacity and usage. This +* is typically useful when buffering I/O data. +*/ + +struct QIOBuffer { +size_t capacity; +size_t offset; +uint8_t *buffer; +}; + +/** +* qio_buffer_reserve: +* @buffer: the buffer object +* @len: the minimum required free space +* +* Ensure that the buffer has space allocated for at least +* @len bytes. If the current buffer is too small, it will +* be reallocated, possibly to a larger size than requested. +*/ +void qio_buffer_reserve (QIOBuffer *buffer,size_t len); + +/** +* qio_buffer_reset: +* @buffer: the buffer object +* +* Reset the length of the stored data to zero, but do +* not free / reallocate the memory buffer +*/ +void qio_buffer_reset (QIOBuffer *buffer); + +/** +* qio_buffer_free: +* @buffer: the buffer object +* +* Reset the length of the stored data to zero and also +* free the internal memory buffer +*/ +void qio_buffer_free (QIOBuffer *buffer); + +/** +* qio_buffer_append: +* @buffer: the buffer object +* @data: the data block to append +* @len: the length of @data in bytes +* +* Append the contents of @data to the end of the buffer. +* The caller must ensure that the buffer has sufficient +* free space for @len bytes, typically by calling the +* qio_buffer_reserve() method prior to appending. +*/ +void qio_buffer_append (QIOBuffer *buffer,const void *data,size_t len); + +/** +* qio_buffer_advance: +* @buffer: the buffer object +* @len: the number of bytes to skip +* +* Remove @len bytes of data from the head of the buffer. +* The internal buffer will not be reallocated, so will +* have at least @len bytes of free space after this +* call completes +*/ +void qio_buffer_advance (QIOBuffer *buffer,size_t len); + +/** +* qio_buffer_end: +* @buffer: the buffer object +* +* Get a pointer to the tail end of the internal buffer +* The returned pointer is only valid until the next +* call to qio_buffer_reserve(). +* +* Returns: the tail of the buffer +*/ +uint8_t *qio_buffer_end (QIOBuffer *buffer); + +/** +* qio_buffer_empty: +* @buffer: the buffer object +* +* Determine if the buffer contains any current data +* +* Returns: true if the buffer holds data, false otherwise +*/ +gboolean qio_buffer_empty (QIOBuffer *buffer); + +#endif /* QIO_BUFFER_H__ */
io/Makefile.objs +1 -0
@@ -1,3 +1,4 @@ +util-obj-y += buffer.o util-obj-y += task.o util-obj-y += channel.o util-obj-y += channel-unix.o
io/buffer.c new file +65 -0
65 lines not on this page · raw message
ui/vnc-auth-sasl.c +2 -2
@@ -113,8 +113,8 @@ long vnc_client_read_sasl(VncState *vs)return vnc_client_io_error (vs,-1 , -EIO );VNC_DEBUG ("Read SASL Encoded %p size %ld Decoded %p size %d\n" , encoded, ret, decoded, decodedLen); -buffer_reserve (&vs->input, decodedLen); -buffer_append (&vs->input, decoded, decodedLen); +qio_buffer_reserve (&vs->input, decodedLen); +qio_buffer_append (&vs->input, decoded, decodedLen);return decodedLen; }
ui/vnc-enc-tight.c +19 -19
@@ -856,7 +856,7 @@ static int tight_compress_data(VncState *vs, int stream_id, size_t bytes, }/* reserve memory in output buffer */ -buffer_reserve (&vs->tight.zlib, bytes +64 ); +qio_buffer_reserve (&vs->tight.zlib, bytes +64 );/* set pointers */ zstream->next_in = vs->tight.tight.buffer;
@@ -879,7 +879,7 @@ static int tight_compress_data(VncState *vs, int stream_id, size_t bytes,tight_send_compact_size (vs, bytes);vnc_write (vs, vs->tight.zlib.buffer, bytes); -buffer_reset (&vs->tight.zlib); +qio_buffer_reset (&vs->tight.zlib);return bytes; }
@@ -1053,7 +1053,7 @@ static bool send_gradient_rect(VncState *vs, int x, int y, int w, int h)vnc_write_u8 (vs, (stream |VNC_TIGHT_EXPLICIT_FILTER ) <<4 );vnc_write_u8 (vs,VNC_TIGHT_FILTER_GRADIENT ); -buffer_reserve (&vs->tight.gradient, w *3 *sizeof (int )); +qio_buffer_reserve (&vs->tight.gradient, w *3 *sizeof (int ));if (vs->tight.pixel24) {tight_filter_gradient24 (vs, vs->tight.tight.buffer, w, h);
@@ -1066,7 +1066,7 @@ static bool send_gradient_rect(VncState *vs, int x, int y, int w, int h) bytes =2 ; } -buffer_reset (&vs->tight.gradient); +qio_buffer_reset (&vs->tight.gradient); bytes = w * h * bytes; vs->tight.tight.offset = bytes;
@@ -1149,7 +1149,7 @@ static int send_palette_rect(VncState *vs, int x, int y,static void jpeg_init_destination (j_compress_ptr cinfo) {VncState *vs = cinfo->client_data; -Buffer *buffer = &vs->tight.jpeg; +QIOBuffer *buffer = &vs->tight.jpeg; cinfo->dest->next_output_byte = (JOCTET *)buffer->buffer + buffer->offset; cinfo->dest->free_in_buffer = (size_t )(buffer->capacity - buffer->offset);
@@ -1159,10 +1159,10 @@ static void jpeg_init_destination(j_compress_ptr cinfo)static boolean jpeg_empty_output_buffer (j_compress_ptr cinfo) {VncState *vs = cinfo->client_data; -Buffer *buffer = &vs->tight.jpeg; +QIOBuffer *buffer = &vs->tight.jpeg; buffer->offset = buffer->capacity; -buffer_reserve (buffer,2048 ); +qio_buffer_reserve (buffer,2048 );jpeg_init_destination (cinfo);return TRUE; }
@@ -1171,7 +1171,7 @@ static boolean jpeg_empty_output_buffer(j_compress_ptr cinfo)static void jpeg_term_destination (j_compress_ptr cinfo) {VncState *vs = cinfo->client_data; -Buffer *buffer = &vs->tight.jpeg; +QIOBuffer *buffer = &vs->tight.jpeg; buffer->offset = buffer->capacity - cinfo->dest->free_in_buffer; }
@@ -1190,7 +1190,7 @@ static int send_jpeg_rect(VncState *vs, int x, int y, int w, int h, int quality)return send_full_color_rect (vs, x, y, w, h); } -buffer_reserve (&vs->tight.jpeg,2048 ); +qio_buffer_reserve (&vs->tight.jpeg,2048 ); cinfo.err =jpeg_std_error (&jerr);jpeg_create_compress (&cinfo);
@@ -1227,7 +1227,7 @@ static int send_jpeg_rect(VncState *vs, int x, int y, int w, int h, int quality)tight_send_compact_size (vs, vs->tight.jpeg.offset);vnc_write (vs, vs->tight.jpeg.buffer, vs->tight.jpeg.offset); -buffer_reset (&vs->tight.jpeg); +qio_buffer_reset (&vs->tight.jpeg);return 1 ; }
@@ -1270,7 +1270,7 @@ static void png_write_data(png_structp png_ptr, png_bytep data, {VncState *vs =png_get_io_ptr (png_ptr); -buffer_reserve (&vs->tight.png, vs->tight.png.offset + length); +qio_buffer_reserve (&vs->tight.png, vs->tight.png.offset + length);memcpy (vs->tight.png.buffer + vs->tight.png.offset, data, length); vs->tight.png.offset += length;
@@ -1351,7 +1351,7 @@ static int send_png_rect(VncState *vs, int x, int y, int w, int h,png_write_info (png_ptr, info_ptr); -buffer_reserve (&vs->tight.png,2048 ); +qio_buffer_reserve (&vs->tight.png,2048 ); linebuf =qemu_pixman_linebuf_create (PIXMAN_BE_r8g8b8, w); buf = (uint8_t *)pixman_image_get_data (linebuf);for (dy =0 ; dy < h; dy++)
@@ -1377,14 +1377,14 @@ static int send_png_rect(VncState *vs, int x, int y, int w, int h,tight_send_compact_size (vs, vs->tight.png.offset);vnc_write (vs, vs->tight.png.buffer, vs->tight.png.offset); -buffer_reset (&vs->tight.png); +qio_buffer_reset (&vs->tight.png); return 1; }#endif /* CONFIG_VNC_PNG */ static void vnc_tight_start (VncState *vs) { -buffer_reset (&vs->tight.tight); +qio_buffer_reset (&vs->tight.tight);// make the output buffer be the zlib buffer, so we can compress it later vs->tight.tmp = vs->output;
@@ -1686,13 +1686,13 @@ void vnc_tight_clear(VncState *vs) } } -buffer_free (&vs->tight.tight); -buffer_free (&vs->tight.zlib); -buffer_free (&vs->tight.gradient); +qio_buffer_free (&vs->tight.tight); +qio_buffer_free (&vs->tight.zlib); +qio_buffer_free (&vs->tight.gradient);#ifdef CONFIG_VNC_JPEG -buffer_free (&vs->tight.jpeg); +qio_buffer_free (&vs->tight.jpeg);#endif #ifdef CONFIG_VNC_PNG -buffer_free (&vs->tight.png); +qio_buffer_free (&vs->tight.png);#endif }
ui/vnc-enc-zlib.c +3 -3
@@ -47,7 +47,7 @@ void vnc_zlib_zfree(void *x, void *addr)static void vnc_zlib_start (VncState *vs) { -buffer_reset (&vs->zlib.zlib); +qio_buffer_reset (&vs->zlib.zlib);// make the output buffer be the zlib buffer, so we can compress it later vs->zlib.tmp = vs->output;
@@ -96,7 +96,7 @@ static int vnc_zlib_stop(VncState *vs) }// reserve memory in output buffer -buffer_reserve (&vs->output, vs->zlib.zlib.offset +64 ); +qio_buffer_reserve (&vs->output, vs->zlib.zlib.offset +64 );// set pointers zstream->next_in = vs->zlib.zlib.buffer;
@@ -148,5 +148,5 @@ void vnc_zlib_clear(VncState *vs)if (vs->zlib.stream.opaque) {deflateEnd (&vs->zlib.stream); } -buffer_free (&vs->zlib.zlib); +qio_buffer_free (&vs->zlib.zlib); }
ui/vnc-enc-zrle.c +9 -9
@@ -36,7 +36,7 @@ static const int bits_per_packed_pixel[] = {static void vnc_zrle_start (VncState *vs) { -buffer_reset (&vs->zrle.zrle); +qio_buffer_reset (&vs->zrle.zrle);/* make the output buffer be the zlib buffer, so we can compress it later */ vs->zrle.tmp = vs->output;
@@ -53,10 +53,10 @@ static void vnc_zrle_stop(VncState *vs)static void *zrle_convert_fb (VncState *vs,int x,int y,int w,int h,int bpp) { -Buffer tmp; +QIOBuffer tmp; -buffer_reset (&vs->zrle.fb); -buffer_reserve (&vs->zrle.fb, w * h * bpp + bpp); +qio_buffer_reset (&vs->zrle.fb); +qio_buffer_reserve (&vs->zrle.fb, w * h * bpp + bpp); tmp = vs->output; vs->output = vs->zrle.fb;
@@ -72,7 +72,7 @@ static int zrle_compress_data(VncState *vs, int level) {z_streamp zstream = &vs->zrle.stream; -buffer_reset (&vs->zrle.zlib); +qio_buffer_reset (&vs->zrle.zlib);if (zstream->opaque != vs) {int err;
@@ -92,7 +92,7 @@ static int zrle_compress_data(VncState *vs, int level) }/* reserve memory in output buffer */ -buffer_reserve (&vs->zrle.zlib, vs->zrle.zrle.offset +64 ); +qio_buffer_reserve (&vs->zrle.zlib, vs->zrle.zrle.offset +64 );/* set pointers */ zstream->next_in = vs->zrle.zrle.buffer;
@@ -360,7 +360,7 @@ void vnc_zrle_clear(VncState *vs)if (vs->zrle.stream.opaque) {deflateEnd (&vs->zrle.stream); } -buffer_free (&vs->zrle.zrle); -buffer_free (&vs->zrle.fb); -buffer_free (&vs->zrle.zlib); +qio_buffer_free (&vs->zrle.zrle); +qio_buffer_free (&vs->zrle.fb); +qio_buffer_free (&vs->zrle.zlib); }
ui/vnc-jobs.c +8 -7
@@ -53,7 +53,7 @@ struct VncJobQueue {QemuCond cond;QemuMutex mutex;QemuThread thread; -Buffer buffer; +QIOBuffer buffer;bool exit; QTAILQ_HEAD(, VncJob) jobs; };
@@ -166,7 +166,7 @@ void vnc_jobs_consume_buffer(VncState *vs)vnc_lock_output (vs);if (vs->jobs_buffer.offset) {vnc_write (vs, vs->jobs_buffer.buffer, vs->jobs_buffer.offset); -buffer_reset (&vs->jobs_buffer); +qio_buffer_reset (&vs->jobs_buffer); } flush = vs->csock !=-1 && vs->abort != true;vnc_unlock_output (vs);
@@ -195,7 +195,7 @@ static void vnc_async_encoding_start(VncState *orig, VncState *local) local->output = queue->buffer; local->csock =-1 ;/* Don't do any network work on this thread */ -buffer_reset (&local->output); +qio_buffer_reset (&local->output); }static void vnc_async_encoding_end (VncState *orig,VncState *local)
@@ -272,10 +272,11 @@ static int vnc_worker_thread_loop(VncJobQueue *queue) vs.output.buffer[saved_offset +1 ] = n_rectangles &0xFF ;vnc_lock_output (job->vs); +if (job->vs->csock !=-1 ) { -buffer_reserve (&job->vs->jobs_buffer, vs.output.offset); -buffer_append (&job->vs->jobs_buffer, vs.output.buffer, - vs.output.offset); +qio_buffer_reserve (&job->vs->jobs_buffer, vs.output.offset); +qio_buffer_append (&job->vs->jobs_buffer, vs.output.buffer, + vs.output.offset);/* Copy persistent encoding data */ vnc_async_encoding_end (job->vs, &vs);
@@ -309,7 +310,7 @@ static void vnc_queue_clear(VncJobQueue *q) {qemu_cond_destroy (&queue->cond);qemu_mutex_destroy (&queue->mutex); -buffer_free (&queue->buffer); +qio_buffer_free (&queue->buffer);g_free (q); queue =NULL ;/* Unset global queue */ }
ui/vnc-ws.c +18 -18
@@ -85,8 +85,8 @@ void vncws_handshake_read(void *opaque)/* Typical HTTP headers from novnc are 512 bytes, so limiting * total header size to 4096 is easily enough. */ size_t want =4096 - vs->ws_input.offset; -buffer_reserve (&vs->ws_input, want); - ret =vnc_client_read_buf (vs,buffer_end (&vs->ws_input), want); +qio_buffer_reserve (&vs->ws_input, want); + ret =vnc_client_read_buf (vs,qio_buffer_end (&vs->ws_input), want);if (!ret) {if (vs->csock ==-1 ) {
@@ -101,7 +101,7 @@ void vncws_handshake_read(void *opaque)if (handshake_end) {qemu_set_fd_handler2 (vs->csock,NULL , vnc_client_read,NULL , vs);vncws_process_handshake (vs, vs->ws_input.buffer, vs->ws_input.offset); -buffer_advance (&vs->ws_input, handshake_end - vs->ws_input.buffer + +qio_buffer_advance (&vs->ws_input, handshake_end - vs->ws_input.buffer +strlen (WS_HANDSHAKE_END )); }else if (vs->ws_input.offset >=4096 ) {VNC_DEBUG ("End of headers not found in first 4096 bytes\n" );
@@ -117,8 +117,8 @@ long vnc_client_read_ws(VncState *vs)size_t payload_size, header_size;VNC_DEBUG ("Read websocket %p size %zd offset %zd\n" , vs->ws_input.buffer, vs->ws_input.capacity, vs->ws_input.offset); -buffer_reserve (&vs->ws_input,4096 ); - ret =vnc_client_read_buf (vs,buffer_end (&vs->ws_input),4096 ); +qio_buffer_reserve (&vs->ws_input,4096 ); + ret =vnc_client_read_buf (vs,qio_buffer_end (&vs->ws_input),4096 );if (!ret) {return 0 ; }
@@ -136,7 +136,7 @@ long vnc_client_read_ws(VncState *vs)return err; } -buffer_advance (&vs->ws_input, header_size); +qio_buffer_advance (&vs->ws_input, header_size); }if (vs->ws_payload_remain !=0 ) { err = vncws_decode_frame_payload(&vs->ws_input,
@@ -152,10 +152,10 @@ long vnc_client_read_ws(VncState *vs) } ret += err; -buffer_reserve (&vs->input, payload_size); -buffer_append (&vs->input, payload, payload_size); +qio_buffer_reserve (&vs->input, payload_size); +qio_buffer_append (&vs->input, payload, payload_size); -buffer_advance (&vs->ws_input, payload_size); +qio_buffer_advance (&vs->ws_input, payload_size); } }while (vs->ws_input.offset >0 );
@@ -168,13 +168,13 @@ long vnc_client_write_ws(VncState *vs)VNC_DEBUG ("Write WS: Pending output %p size %zd offset %zd\n" , vs->output.buffer, vs->output.capacity, vs->output.offset);vncws_encode_frame (&vs->ws_output, vs->output.buffer, vs->output.offset); -buffer_reset (&vs->output); +qio_buffer_reset (&vs->output); ret =vnc_client_write_buf (vs, vs->ws_output.buffer, vs->ws_output.offset);if (!ret) {return 0 ; } -buffer_advance (&vs->ws_output, ret); +qio_buffer_advance (&vs->ws_output, ret);if (vs->ws_output.offset ==0 ) {qemu_set_fd_handler2 (vs->csock,NULL , vnc_client_read,NULL , vs);
@@ -257,8 +257,8 @@ void vncws_process_handshake(VncState *vs, uint8_t *line, size_t size)g_free (key); } -void vncws_encode_frame (Buffer *output,const void *payload, -const size_t payload_size) +void vncws_encode_frame (QIOBuffer *output,const void *payload, +const size_t payload_size) {size_t header_size =0 ;unsigned char opcode =WS_OPCODE_BINARY_FRAME ;
@@ -285,12 +285,12 @@ void vncws_encode_frame(Buffer *output, const void *payload, header_size =10 ; } -buffer_reserve (output, header_size + payload_size); -buffer_append (output, header.buf, header_size); -buffer_append (output, payload, payload_size); +qio_buffer_reserve (output, header_size + payload_size); +qio_buffer_append (output, header.buf, header_size); +qio_buffer_append (output, payload, payload_size); } -int vncws_decode_frame_header (Buffer *input, +int vncws_decode_frame_header (QIOBuffer *input,size_t *header_size,size_t *payload_remain,WsMask *payload_mask)
@@ -344,7 +344,7 @@ int vncws_decode_frame_header(Buffer *input,return 1 ; } -int vncws_decode_frame_payload (Buffer *input, +int vncws_decode_frame_payload (QIOBuffer *input,size_t *payload_remain,WsMask *payload_mask,uint8_t **payload,size_t *payload_size) {
ui/vnc-ws.h +3 -3
@@ -79,13 +79,13 @@ void vncws_handshake_read(void *opaque);long vnc_client_write_ws (VncState *vs);long vnc_client_read_ws (VncState *vs);void vncws_process_handshake (VncState *vs,uint8_t *line,size_t size); -void vncws_encode_frame (Buffer *output,const void *payload, +void vncws_encode_frame (QIOBuffer *output,const void *payload,const size_t payload_size); -int vncws_decode_frame_header (Buffer *input, +int vncws_decode_frame_header (QIOBuffer *input,size_t *header_size,size_t *payload_remain,WsMask *payload_mask); -int vncws_decode_frame_payload (Buffer *input, +int vncws_decode_frame_payload (QIOBuffer *input,size_t *payload_remain,WsMask *payload_mask,uint8_t **payload,size_t *payload_size);
ui/vnc.c +12 -55
113 lines not on this page · raw message
ui/vnc.h +18 -32
101 lines not on this page · raw message
Signature · 1 line
2.1.0
RFC PATCH 28/34 io: add QIOChannelWebsock class
In reply to Daniel P. BerrangeAdd a QIOChannel subclass that can run the websocket protocol over the top of another QIOChannel instance.
--- include/io/channel-websock.h | 108 +++++ io/Makefile.objs | 1 + io/channel-websock.c | 976 +++++++++++++++++++++++++++++++++++++++++++ 3 files changed, 1085 insertions(+) create mode 100644 include/io/channel-websock.h create mode 100644 io/channel-websock.c
include/io/channel-websock.h new file +108 -0
@@ -0,0 +1,108 @@ +/* +* QEMU I/O channels driver websockets +* +* Copyright (c) 2015 Red Hat, Inc. +* +* This library is free software; you can redistribute it and/or +* modify it under the terms of the GNU Lesser General Public +* License as published by the Free Software Foundation; either +* version 2 of the License, or (at your option) any later version. +* +* This library is distributed in the hope that it will be useful, +* but WITHOUT ANY WARRANTY; without even the implied warranty of +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +* Lesser General Public License for more details. +* +* You should have received a copy of the GNU Lesser General Public +* License along with this library; if not, see <http://www.gnu.org/licenses/>. +* +*/ + +#ifndef QIO_CHANNEL_WEBSOCK_H__ +#define QIO_CHANNEL_WEBSOCK_H__ + +#include "io/channel.h" +#include "io/buffer.h" +#include "io/task.h" + +#define TYPE_QIO_CHANNEL_WEBSOCK "qemu:io-channel-websock" +#define QIO_CHANNEL_WEBSOCK (obj) \ + OBJECT_CHECK(QIOChannelWebsock, (obj), TYPE_QIO_CHANNEL_WEBSOCK) + +typedef struct QIOChannelWebsock QIOChannelWebsock ; +typedef union QIOChannelWebsockMask QIOChannelWebsockMask ; + +union QIOChannelWebsockMask { +char c[4 ]; +uint32_t u; +}; + +/** +* QIOChannelWebsock +* +* The QIOChannelWebsock class provides a channel wrapper which +* can transparently run the HTTP websockets protocol. This is +* usually used over a TCP socket, but there is actually no +* technical restriction on which type of master channel is +* used as the transport. +* +* This channel object is currently only capable of running as +* a websocket server and is a pretty crude implementation +* of it, not supporting the full websockets protocol feature +* set. It is sufficient to use with a simple websockets +* client for encapsulating VNC for noVNC in-browser client. +*/ + +struct QIOChannelWebsock { +QIOChannel parent; +QIOChannel *master; +QIOBuffer encinput; +QIOBuffer encoutput; +QIOBuffer rawinput; +QIOBuffer rawoutput; +size_t payload_remain; +QIOChannelWebsockMask mask; +guint io_tag; +Error *io_err; +gboolean io_eof; +}; + +/** +* qio_channel_websock_new_server: +* @master: the underlying channel object +* +* Create a new websockets channel that runs the server +* side of the protocol. +* +* After creating the channel, it is mandatory to call +* the qio_channel_websock_handshake() method before attempting +* todo any I/O on the channel. +* +* Once the handshake has completed, all I/O should be done +* via the new websocket channel object and not the original +* master channel +* +* Returns: the new websockets channel object +*/ +QIOChannelWebsock * +qio_channel_websock_new_server (QIOChannel *master); + +/** +* qio_channel_websock_handshake: +* @ioc: the websocket channel object +* @func: the callback to invoke when completed +* @opaque: opaque data to pass to @func +* @destroy: optional callback to free @opaque +* +* Perform the websocket handshake. This method +* will return immediately and the handshake will +* continue in the background, provided the main +* loop is running. When the handshake is complete, +* or fails, the @func callback will be invoked. +*/ +void qio_channel_websock_handshake (QIOChannelWebsock *ioc, +QIOTaskFunc func, +gpointer opaque, +GDestroyNotify destroy); + +#endif /* QIO_CHANNEL_WEBSOCK_H__ */
io/Makefile.objs +1 -0
@@ -5,3 +5,4 @@ util-obj-y += channel-unix.o util-obj-y += channel-socket.o util-obj-y += channel-file.o util-obj-y += channel-tls.o +util-obj-y += channel-websock.o
io/channel-websock.c new file +976 -0
976 lines not on this page · raw message
Signature · 1 line
2.1.0
RFC PATCH 29/34 ui: convert VNC server to use QEMUIOChannelSocket classes
In reply to Daniel P. BerrangeThe minimal first step conversion to use QEMUIOChannelSocket classes instead of directly using POSIX sockets API. This will later be extended to also cover the TLS, SASL and websockets code.
--- ui/vnc-auth-sasl.c | 39 ++-- ui/vnc-auth-vencrypt.c | 45 ++-- ui/vnc-jobs.c | 12 +- ui/vnc-tls.c | 24 +-- ui/vnc-ws.c | 53 +++-- ui/vnc-ws.h | 8 +- ui/vnc.c | 545 +++++++++++++++++++++++++------------------------ ui/vnc.h | 33 ++- 8 files changed, 422 insertions(+), 337 deletions(-)
ui/vnc-auth-sasl.c +25 -14
@@ -62,7 +62,7 @@ long vnc_client_write_sasl(VncState *vs) (const char **)&vs->sasl.encoded, &vs->sasl.encodedLength);if (err !=SASL_OK ) -return vnc_client_io_error (vs,-1 ,EIO ); +return vnc_client_io_error (vs,-1 ,NULL ); vs->sasl.encodedOffset =0 ; }
@@ -86,7 +86,11 @@ long vnc_client_write_sasl(VncState *vs)* SASL encoded output */ if (vs->output.offset ==0 ) { -qemu_set_fd_handler2 (vs->csock,NULL , vnc_client_read,NULL , vs); +if (vs->ioc_tag) { +g_source_remove (vs->ioc_tag); + } + vs->ioc_tag =qio_channel_add_watch ( + vs->ioc,G_IO_IN , vnc_client_io, vs); }return ret;
@@ -110,7 +114,7 @@ long vnc_client_read_sasl(VncState *vs) &decoded, &decodedLen);if (err !=SASL_OK ) -return vnc_client_io_error (vs,-1 , -EIO ); +return vnc_client_io_error (vs,-1 ,NULL );VNC_DEBUG ("Read SASL Encoded %p size %ld Decoded %p size %d\n" , encoded, ret, decoded, decodedLen);qio_buffer_reserve (&vs->input, decodedLen);
@@ -255,17 +259,17 @@ static int protocol_client_auth_sasl_step(VncState *vs, uint8_t *data, size_t levnc_read_when (vs, protocol_client_auth_sasl_step_len,4 ); }else {if (!vnc_auth_sasl_check_ssf (vs)) { -VNC_DEBUG ("Authentication rejected for weak SSF %d\n" , vs->csock); +VNC_DEBUG ("Authentication rejected for weak SSF %p\n" , vs->ioc); goto authreject; }/* Check username whitelist ACL */ if (vnc_auth_sasl_check_access (vs) <0 ) { -VNC_DEBUG ("Authentication rejected for ACL %d\n" , vs->csock); +VNC_DEBUG ("Authentication rejected for ACL %p\n" , vs->ioc); goto authreject; } -VNC_DEBUG ("Authentication successful %d\n" , vs->csock); +VNC_DEBUG ("Authentication successful %p\n" , vs->ioc);vnc_write_u32 (vs,0 );/* Accept auth */ /* * Delay writing in SSF encoded mode until pending output
@@ -383,17 +387,17 @@ static int protocol_client_auth_sasl_start(VncState *vs, uint8_t *data, size_t lvnc_read_when (vs, protocol_client_auth_sasl_step_len,4 ); }else {if (!vnc_auth_sasl_check_ssf (vs)) { -VNC_DEBUG ("Authentication rejected for weak SSF %d\n" , vs->csock); +VNC_DEBUG ("Authentication rejected for weak SSF %p\n" , vs->ioc); goto authreject; }/* Check username whitelist ACL */ if (vnc_auth_sasl_check_access (vs) <0 ) { -VNC_DEBUG ("Authentication rejected for ACL %d\n" , vs->csock); +VNC_DEBUG ("Authentication rejected for ACL %p\n" , vs->ioc); goto authreject; } -VNC_DEBUG ("Authentication successful %d\n" , vs->csock); +VNC_DEBUG ("Authentication successful %p\n" , vs->ioc);vnc_write_u32 (vs,0 );/* Accept auth */ start_client_init (vs); }
@@ -495,13 +499,16 @@ void start_auth_sasl(VncState *vs)char *localAddr, *remoteAddr;int mechlistlen; -VNC_DEBUG ("Initialize SASL auth %d\n" , vs->csock); +VNC_DEBUG ("Initialize SASL auth %p\n" , vs->ioc);/* Get local & remote client addresses in form IPADDR;PORT */ -if (!(localAddr =vnc_socket_local_addr ("%s;%s" , vs->csock))) + localAddr =vnc_socket_local_addr (vs->sioc,"%s;%s" ,NULL ); +if (!localAddr) { goto authabort; + } -if (!(remoteAddr =vnc_socket_remote_addr ("%s;%s" , vs->csock))) { + remoteAddr =vnc_socket_remote_addr (vs->sioc,"%s;%s" ,NULL ); +if (!remoteAddr) {g_free (localAddr); goto authabort; }
@@ -533,7 +540,8 @@ void start_auth_sasl(VncState *vs)sasl_ssf_t ssf; cipher =gnutls_cipher_get (vs->tls.session); -if (!(ssf = (sasl_ssf_t )gnutls_cipher_get_key_size (cipher))) { + ssf = (sasl_ssf_t )gnutls_cipher_get_key_size (cipher); +if (!ssf) {VNC_DEBUG ("%s" ,"cannot TLS get cipher size\n" );sasl_dispose (&vs->sasl.conn); vs->sasl.conn =NULL ;
@@ -549,9 +557,12 @@ void start_auth_sasl(VncState *vs) vs->sasl.conn =NULL ; goto authabort; } - } else + }else {#endif /* CONFIG_VNC_TLS */ vs->sasl.wantSSF =1 ; +#ifdef CONFIG_VNC_TLS + } +#endif memset (&secprops,0 ,sizeof secprops);/* Inform SASL that we've got an external SSF layer from TLS */
ui/vnc-auth-vencrypt.c +31 -14
72 lines not on this page · raw message
ui/vnc-jobs.c +6 -6
@@ -168,7 +168,7 @@ void vnc_jobs_consume_buffer(VncState *vs)vnc_write (vs, vs->jobs_buffer.buffer, vs->jobs_buffer.offset);qio_buffer_reset (&vs->jobs_buffer); } - flush = vs->csock !=-1 && vs->abort != true; + flush = vs->ioc !=NULL && vs->abort != true;vnc_unlock_output (vs);if (flush) {
@@ -193,7 +193,8 @@ static void vnc_async_encoding_start(VncState *orig, VncState *local) local->hextile = orig->hextile; local->zrle = orig->zrle; local->output = queue->buffer; - local->csock =-1 ;/* Don't do any network work on this thread */ + local->sioc =NULL ;/* Don't do any network work on this thread */ + local->ioc =NULL ;/* Don't do any network work on this thread */ qio_buffer_reset (&local->output); }
@@ -230,7 +231,7 @@ static int vnc_worker_thread_loop(VncJobQueue *queue) }vnc_lock_output (job->vs); -if (job->vs->csock ==-1 || job->vs->abort == true) { +if (job->vs->ioc ==NULL || job->vs->abort == true) {vnc_unlock_output (job->vs); goto disconnected; }
@@ -250,7 +251,7 @@ static int vnc_worker_thread_loop(VncJobQueue *queue) QLIST_FOREACH_SAFE(entry, &job->rectangles, next, tmp) {int n; -if (job->vs->csock ==-1 ) { +if (job->vs->ioc ==NULL ) {vnc_unlock_display (job->vs->vd);/* Copy persistent encoding data */ vnc_async_encoding_end (job->vs, &vs);
@@ -272,8 +273,7 @@ static int vnc_worker_thread_loop(VncJobQueue *queue) vs.output.buffer[saved_offset +1 ] = n_rectangles &0xFF ;vnc_lock_output (job->vs); - -if (job->vs->csock !=-1 ) { +if (job->vs->ioc !=NULL ) {qio_buffer_reserve (&job->vs->jobs_buffer, vs.output.offset);qio_buffer_append (&job->vs->jobs_buffer, vs.output.buffer, vs.output.offset);
ui/vnc-tls.c +12 -12
@@ -69,13 +69,13 @@ static ssize_t vnc_tls_push(gnutls_transport_ptr_t transport, const void *data, size_t len) {struct VncState *vs = (struct VncState *)transport; -int ret; - - retry: - ret =send (vs->csock, data, len,0 ); +ssize_t ret =qio_channel_write (vs->ioc, data, len,NULL );if (ret <0 ) { -if (errno ==EINTR ) - goto retry; +if (ret ==QIO_CHANNEL_ERR_BLOCK ) { + errno =EAGAIN ; + }else { + errno =EIO ; + }return -1 ; }return ret;
@@ -86,13 +86,13 @@ static ssize_t vnc_tls_pull(gnutls_transport_ptr_t transport, void *data, size_t len) {struct VncState *vs = (struct VncState *)transport; -int ret; - - retry: - ret =qemu_recv (vs->csock, data, len,0 ); +ssize_t ret =qio_channel_read (vs->ioc, data, len,NULL );if (ret <0 ) { -if (errno ==EINTR ) - goto retry; +if (ret ==QIO_CHANNEL_ERR_BLOCK ) { + errno =EAGAIN ; + }else { + errno =EIO ; + }return -1 ; }return ret;
ui/vnc-ws.c +41 -12
117 lines not on this page · raw message
ui/vnc-ws.h +6 -2
@@ -73,9 +73,13 @@ enum { };#ifdef CONFIG_VNC_TLS -void vncws_tls_handshake_io (void *opaque); +gboolean vncws_tls_handshake_io (QIOChannel *ioc, +GIOCondition condition, +void *opaque);#endif /* CONFIG_VNC_TLS */ -void vncws_handshake_read (void *opaque); +gboolean vncws_handshake_io (QIOChannel *ioc, +GIOCondition condition, +void *opaque);long vnc_client_write_ws (VncState *vs);long vnc_client_read_ws (VncState *vs);void vncws_process_handshake (VncState *vs,uint8_t *line,size_t size);
ui/vnc.c +278 -267
925 lines not on this page · raw message
ui/vnc.h +23 -10
@@ -34,6 +34,7 @@#include "audio/audio.h" #include "qemu/bitmap.h" #include "io/buffer.h" +#include "io/channel-socket.h" #include <zlib.h> #include <stdbool.h>
@@ -147,8 +148,10 @@ struct VncDisplayint num_exclusive;int connections_limit;VncSharePolicy share_policy; -int lsock; -int lwebsock; +QIOChannelSocket *lsock; +guint lsock_tag; +QIOChannelSocket *lwebsock; +guint lwebsock_tag;bool ws_enabled;DisplaySurface *ds;DisplayChangeListener dcl;
@@ -251,7 +254,10 @@ struct VncJobstruct VncState { -int csock; +QIOChannelSocket *sioc;/* The underlying socket */ +QIOChannel *ioc;/* The channel currently used for I/O */ +guint ioc_tag; +gboolean disconnecting; DECLARE_BITMAP(dirty[VNC_MAX_HEIGHT], VNC_DIRTY_BITS);uint8_t **lossy_rect;/* Not an Array to avoid costly memcpy in
@@ -504,11 +510,12 @@ enum {*****************************************************************************/ /* Event loop functions */ -void vnc_client_read (void *opaque); -void vnc_client_write (void *opaque); +gboolean vnc_client_io (QIOChannel *ioc, +GIOCondition condition, +void *opaque); -long vnc_client_read_buf (VncState *vs,uint8_t *data,size_t datalen); -long vnc_client_write_buf (VncState *vs,const uint8_t *data,size_t datalen); +ssize_t vnc_client_read_buf (VncState *vs,uint8_t *data,size_t datalen); +ssize_t vnc_client_write_buf (VncState *vs,const uint8_t *data,size_t datalen);/* Protocol I/O functions */ void vnc_write (VncState *vs,const void *data,size_t len);
@@ -527,7 +534,7 @@ uint32_t read_u32(uint8_t *data, size_t offset);/* Protocol stage functions */ void vnc_client_error (VncState *vs); -int vnc_client_io_error (VncState *vs,int ret,int last_errno); +int vnc_client_io_error (VncState *vs,int ret,Error **errp);void start_client_init (VncState *vs);void start_auth_vnc (VncState *vs);
@@ -535,8 +542,14 @@ void start_auth_vnc(VncState *vs);/* Misc helpers */ -char *vnc_socket_local_addr (const char *format,int fd); -char *vnc_socket_remote_addr (const char *format,int fd); +char * +vnc_socket_local_addr (QIOChannelSocket *ioc, +const char *format, +Error **errp); +char * +vnc_socket_remote_addr (QIOChannelSocket *ioc, +const char *format, +Error **errp);static inline uint32_t vnc_has_feature (VncState *vs,int feature) {return (vs->features & (1 << feature));
Signature · 1 line
2.1.0
RFC PATCH 30/34 ui: convert VNC server to use QIOChannelTLS
In reply to Daniel P. BerrangeSwitch VNC server over to using the QIOChannelTLS object for the TLS session. This removes all the VNC specific code for dealing with TLS. This has the nice effect that all the CONFIG_VNC_TLS conditionals go away and the user gets an actual error message when requesting TLS instead of it being silently ignored.
--- configure | 31 ------ qemu-options.hx | 28 ++++- ui/Makefile.objs | 2 +- ui/vnc-auth-sasl.c | 46 +++----- ui/vnc-auth-vencrypt.c | 107 ++++++------------ ui/vnc-tls.h | 69 ------------ ui/vnc-ws.c | 89 +++++++-------- ui/vnc-ws.h | 2 - ui/vnc.c | 296 ++++++++++++++++++++++++++----------------------- ui/vnc.h | 13 +-- 10 files changed, 279 insertions(+), 404 deletions(-) delete mode 100644 ui/vnc-tls.h
configure +0 -31
@@ -241,7 +241,6 @@ vnc="yes" sparse="no" uuid="" vde="" -vnc_tls="" vnc_sasl="" vnc_jpeg="" vnc_png=""
@@ -872,10 +871,6 @@ for opt do ;; --disable-strip) strip_opt="no" ;; - --disable-vnc-tls) vnc_tls="no" - ;; - --enable-vnc-tls) vnc_tls="yes" - ;; --disable-vnc-sasl) vnc_sasl="no" ;; --enable-vnc-sasl) vnc_sasl="yes"
@@ -2369,28 +2364,6 @@ EOF fi fi -########################################## -# VNC TLS/WS detection -if test "$vnc" = "yes" -a "$vnc_tls" != "no" ; then - cat > $TMPC <<EOF -#include <gnutls/gnutls.h> -int main(void) { gnutls_session_t s; gnutls_init(&s, GNUTLS_SERVER); return 0; } -EOF - vnc_tls_cflags=`$pkg_config --cflags gnutls 2> /dev/null` - vnc_tls_libs=`$pkg_config --libs gnutls 2> /dev/null` - if compile_prog "$vnc_tls_cflags" "$vnc_tls_libs" ; then - if test "$vnc_tls" != "no" ; then - vnc_tls=yes - fi - libs_softmmu="$vnc_tls_libs $libs_softmmu" - QEMU_CFLAGS="$QEMU_CFLAGS $vnc_tls_cflags" - else - if test "$vnc_tls" = "yes" ; then - feature_not_found "vnc-tls" "Install gnutls devel" - fi - vnc_tls=no - fi -fi ########################################## # VNC SASL detection
@@ -4432,7 +4405,6 @@ echo "Block whitelist (ro) $block_drv_ro_whitelist" echo "VirtFS support $virtfs" echo "VNC support $vnc" if test "$vnc" = "yes" ; then - echo "VNC TLS support $vnc_tls" echo "VNC SASL support $vnc_sasl" echo "VNC JPEG support $vnc_jpeg" echo "VNC PNG support $vnc_png"
@@ -4628,9 +4600,6 @@ echo "CONFIG_BDRV_RO_WHITELIST=$block_drv_ro_whitelist" >> $config_host_mak if test "$vnc" = "yes" ; then echo "CONFIG_VNC=y" >> $config_host_mak fi -if test "$vnc_tls" = "yes" ; then - echo "CONFIG_VNC_TLS=y" >> $config_host_mak -fi if test "$vnc_sasl" = "yes" ; then echo "CONFIG_VNC_SASL=y" >> $config_host_mak fi
qemu-options.hx +26 -2
@@ -1206,8 +1206,9 @@ By definition the Websocket port is 5700+@var{display}. If @var{host} is specified connections will only be allowed from this host. As an alternative the Websocket port could be specified by using @code{websocket}=@var{port}. -TLS encryption for the Websocket connection is supported if the required -certificates are specified with the VNC option @option{x509}. +If no TLS credentials are provided, the websocket connection runs in +uncrypted mode. If TLS credentials are provided, the websocket connection +requires encrypted client connections. @item password
@@ -1228,6 +1229,20 @@ date and time). You can also use keywords "now" or "never" for the expiration time to allow <protocol> password to expire immediately or never expire. +@item tls-cred=@var{ID} + +Provides the ID of a set of TLS credentials to use to secure the +VNC server. They will apply to both the normal VNC server socket +and the websocket socket (if enabled). Setting TLS credentials +will cause the VNC server socket to enable the VeNCrypt auth +mechanism. The credentials should have been previously created +using the @option{-object qcrypto-tls-cred} argument. + +The @option{tls-cred} parameter obsoletes the @option{tls}, +@option{x509}, and @option{x509verify} options, and as such +it is not permitted to set both new and old type options at +the same time. + @item tls Require that client use TLS when communicating with the VNC server. This
@@ -1235,6 +1250,9 @@ uses anonymous TLS credentials so is susceptible to a man-in-the-middle attack. It is recommended that this option be combined with either the @option{x509} or @option{x509verify} options. +This option is now deprecated in favour of using the @option{tls-cred} +argument. + @item x509=@var{/path/to/certificate/dir} Valid if @option{tls} is specified. Require that x509 credentials are used
@@ -1244,6 +1262,9 @@ to provide authentication of the client when this is used. The path following this option specifies where the x509 certificates are to be loaded from. See the @ref{vnc_security} section for details on generating certificates. +This option is now deprecated in favour of using the @option{tls-cred} +argument. + @item x509verify=@var{/path/to/certificate/dir} Valid if @option{tls} is specified. Require that x509 credentials are used
@@ -1257,6 +1278,9 @@ path following this option specifies where the x509 certificates are to be loaded from. See the @ref{vnc_security} section for details on generating certificates. +This option is now deprecated in favour of using the @option{tls-cred} +argument. + @item sasl Require that the client use SASL to authenticate with the VNC server.
ui/Makefile.objs +1 -1
@@ -2,7 +2,7 @@ vnc-obj-y += vnc.o vnc-obj-y += vnc-enc-zlib.o vnc-enc-hextile.o vnc-obj-y += vnc-enc-tight.o vnc-palette.o vnc-obj-y += vnc-enc-zrle.o -vnc-obj-$(CONFIG_VNC_TLS) += vnc-tls.o vnc-auth-vencrypt.o +vnc-obj-y += vnc-auth-vencrypt.o vnc-obj-$(CONFIG_VNC_SASL) += vnc-auth-sasl.o vnc-obj-y += vnc-ws.o vnc-obj-y += vnc-jobs.o
ui/vnc-auth-sasl.c +18 -28
111 lines not on this page · raw message
ui/vnc-auth-vencrypt.c +35 -72
131 lines not on this page · raw message
ui/vnc-tls.h deleted +0 -69
69 lines not on this page · raw message
ui/vnc-ws.c +39 -50
112 lines not on this page · raw message
ui/vnc-ws.h +0 -2
@@ -72,11 +72,9 @@ enum { WS_OPCODE_PONG = 0xA }; -#ifdef CONFIG_VNC_TLS gboolean vncws_tls_handshake_io (QIOChannel *ioc,GIOCondition condition,void *opaque); -#endif /* CONFIG_VNC_TLS */ gboolean vncws_handshake_io (QIOChannel *ioc,GIOCondition condition,void *opaque);
ui/vnc.c +156 -140
502 lines not on this page · raw message
ui/vnc.h +4 -9
@@ -35,6 +35,7 @@#include "qemu/bitmap.h" #include "io/buffer.h" #include "io/channel-socket.h" +#include "io/channel-tls.h" #include <zlib.h> #include <stdbool.h>
@@ -96,10 +97,7 @@ typedef void VncSendHextileTile(VncState *vs,typedef struct VncDisplay VncDisplay ; -#ifdef CONFIG_VNC_TLS -#include "vnc-tls.h" #include "vnc-auth-vencrypt.h" -#endif #ifdef CONFIG_VNC_SASL #include "vnc-auth-sasl.h" #endif
@@ -178,9 +176,8 @@ struct VncDisplaybool ws_tls;/* Used by websockets */ bool lossy;bool non_adaptive; -#ifdef CONFIG_VNC_TLS -VncDisplayTLS tls; -#endif +QCryptoTLSCreds *creds; +char *tlsaclname;#ifdef CONFIG_VNC_SASL VncDisplaySASL sasl;#endif
@@ -284,9 +281,7 @@ struct VncStateint auth;int subauth;/* Used by VeNCrypt */ char challenge[VNC_AUTH_CHALLENGE_SIZE ]; -#ifdef CONFIG_VNC_TLS -VncStateTLS tls; -#endif +QCryptoTLSSession *tls;/* Borrowed pointer from channel, don't free */ #ifdef CONFIG_VNC_SASL VncStateSASL sasl;#endif
Signature · 1 line
2.1.0
RFC PATCH 31/34 ui: convert VNC server to use QIOChannelWebsock
In reply to Daniel P. BerrangeRemove custom websock handling code from the VNC server and use the QIOChannelWebsock class instead.
--- ui/vnc-ws.c | 329 +++++------------------------------------------------------- ui/vnc-ws.h | 63 ------------ ui/vnc.c | 25 +---- ui/vnc.h | 4 - 4 files changed, 29 insertions(+), 392 deletions(-)
ui/vnc-ws.c +25 -304
@@ -19,12 +19,7 @@*/ #include "vnc.h" -#include "qemu/main-loop.h" -#include "crypto/hash.h" - -#include "qemu/sockets.h" - -static void vncws_handshake_read (VncState *vs); +#include "io/channel-websock.h" static void vncws_tls_handshake_done (QIOTask *task,gpointer user_data)
@@ -35,6 +30,7 @@ static void vncws_tls_handshake_done(QIOTask *task,VNC_DEBUG ("Handshake failed %s\n" ,error_get_pretty (task->err));vnc_client_error (vs); }else { +VNC_DEBUG ("TLS handshake complete, starting websocket handshake\n" ); vs->ioc_tag =qio_channel_add_watch (QIO_CHANNEL (vs->ioc),G_IO_IN , vncws_handshake_io, vs); }
@@ -79,38 +75,20 @@ gboolean vncws_tls_handshake_io(QIOChannel *ioc G_GNUC_UNUSED,return TRUE; } -static void vncws_handshake_read (VncState *vs) -{ -uint8_t *handshake_end; -long ret; -/* Typical HTTP headers from novnc are 512 bytes, so limiting -* total header size to 4096 is easily enough. */ -size_t want =4096 - vs->ws_input.offset; -qio_buffer_reserve (&vs->ws_input, want); - ret =vnc_client_read_buf (vs,qio_buffer_end (&vs->ws_input), want); -if (!ret) { -if (vs->disconnecting) { -vnc_disconnect_finish (vs); - } -return ; - } - vs->ws_input.offset += ret; +static void vncws_handshake_done (QIOTask *task, +gpointer user_data) +{ +struct VncState *vs = user_data; - handshake_end = (uint8_t *)g_strstr_len ((char *)vs->ws_input.buffer, - vs->ws_input.offset,WS_HANDSHAKE_END ); -if (handshake_end) { -if (vs->ioc_tag) { -g_source_remove (vs->ioc_tag); - } +if (task->err) { +VNC_DEBUG ("Websock handshake failed %s\n" ,error_get_pretty (task->err)); +vnc_client_error (vs); + }else { +VNC_DEBUG ("Websock handshake complete, starting VNC protocol\n" ); +vnc_init_state (vs); vs->ioc_tag =qio_channel_add_watch ( vs->ioc,G_IO_IN , vnc_client_io, vs); -vncws_process_handshake (vs, vs->ws_input.buffer, vs->ws_input.offset); -qio_buffer_advance (&vs->ws_input, handshake_end - vs->ws_input.buffer + -strlen (WS_HANDSHAKE_END )); - }else if (vs->ws_input.offset >=4096 ) { -VNC_DEBUG ("End of headers not found in first 4096 bytes\n" ); -vnc_client_error (vs); } }
@@ -120,280 +98,23 @@ gboolean vncws_handshake_io(QIOChannel *ioc G_GNUC_UNUSED, void *opaque) {VncState *vs = opaque; -vncws_handshake_read (vs); -return TRUE; -} - -long vnc_client_read_ws (VncState *vs) -{ -int ret, err; -uint8_t *payload; -size_t payload_size, header_size; -VNC_DEBUG ("Read websocket %p size %zd offset %zd\n" , vs->ws_input.buffer, - vs->ws_input.capacity, vs->ws_input.offset); -qio_buffer_reserve (&vs->ws_input,4096 ); - ret =vnc_client_read_buf (vs,qio_buffer_end (&vs->ws_input),4096 ); -if (!ret) { -return 0 ; - } - vs->ws_input.offset += ret; - - ret =0 ; -/* consume as much of ws_input buffer as possible */ -do { -if (vs->ws_payload_remain ==0 ) { - err =vncws_decode_frame_header (&vs->ws_input, - &header_size, - &vs->ws_payload_remain, - &vs->ws_payload_mask); -if (err <=0 ) { -return err; - } - -qio_buffer_advance (&vs->ws_input, header_size); - } -if (vs->ws_payload_remain !=0 ) { - err =vncws_decode_frame_payload (&vs->ws_input, - &vs->ws_payload_remain, - &vs->ws_payload_mask, - &payload, - &payload_size); -if (err <0 ) { -return err; - } -if (err ==0 ) { -return ret; - } - ret += err; - -qio_buffer_reserve (&vs->input, payload_size); -qio_buffer_append (&vs->input, payload, payload_size); - -qio_buffer_advance (&vs->ws_input, payload_size); - } - }while (vs->ws_input.offset >0 ); - -return ret; -} - -long vnc_client_write_ws (VncState *vs) -{ -long ret; -VNC_DEBUG ("Write WS: Pending output %p size %zd offset %zd\n" , - vs->output.buffer, vs->output.capacity, vs->output.offset); -vncws_encode_frame (&vs->ws_output, vs->output.buffer, vs->output.offset); -qio_buffer_reset (&vs->output); - ret =vnc_client_write_buf (vs, vs->ws_output.buffer, vs->ws_output.offset); -if (!ret) { -return 0 ; - } - -qio_buffer_advance (&vs->ws_output, ret); - -if (vs->ws_output.offset ==0 ) { -if (vs->ioc_tag) { -g_source_remove (vs->ioc_tag); - } - vs->ioc_tag =qio_channel_add_watch ( - vs->ioc,G_IO_IN , vnc_client_io, vs); - } - -return ret; -} - -static char *vncws_extract_handshake_entry (const char *handshake, -size_t handshake_len,const char *name) -{ -char *begin, *end, *ret =NULL ; -char *line =g_strdup_printf ("%s%s: " ,WS_HANDSHAKE_DELIM , name); - begin =g_strstr_len (handshake, handshake_len, line); -if (begin !=NULL ) { - begin +=strlen (line); - end =g_strstr_len (begin, handshake_len - (begin - handshake), -WS_HANDSHAKE_DELIM ); -if (end !=NULL ) { - ret =g_strndup (begin, end - begin); - } - } -g_free (line); -return ret; -} - -static void vncws_send_handshake_response (VncState *vs,const char * key) -{ -char combined_key[WS_CLIENT_KEY_LEN +WS_GUID_LEN +1 ]; -char *accept =NULL , *response =NULL ; -Error *err =NULL ; - -g_strlcpy (combined_key, key,WS_CLIENT_KEY_LEN +1 ); -g_strlcat (combined_key,WS_GUID ,WS_CLIENT_KEY_LEN +WS_GUID_LEN +1 ); - -/* hash and encode it */ -if (qcrypto_hash_base64 (QCRYPTO_HASH_ALG_SHA1 , - combined_key, -WS_CLIENT_KEY_LEN +WS_GUID_LEN , - &accept, - &err) <0 ) { -VNC_DEBUG ("Hashing Websocket combined key failed %s\n" , -error_get_pretty (err)); -error_free (err); -vnc_client_error (vs); -return ; - } - - response =g_strdup_printf (WS_HANDSHAKE , accept); -vnc_client_write_buf (vs, (const uint8_t *)response,strlen (response)); +QIOChannelWebsock *wioc; -g_free (accept); -g_free (response); - - vs->encode_ws =1 ; -vnc_init_state (vs); -} - -void vncws_process_handshake (VncState *vs,uint8_t *line,size_t size) -{ -char *protocols =vncws_extract_handshake_entry ((const char *)line, size, -"Sec-WebSocket-Protocol" ); -char *version =vncws_extract_handshake_entry ((const char *)line, size, -"Sec-WebSocket-Version" ); -char *key =vncws_extract_handshake_entry ((const char *)line, size, -"Sec-WebSocket-Key" ); - -if (protocols && version && key - &&g_strrstr (protocols,"binary" ) - && !strcmp (version,WS_SUPPORTED_VERSION ) - &&strlen (key) ==WS_CLIENT_KEY_LEN ) { -vncws_send_handshake_response (vs, key); - }else { -VNC_DEBUG ("Defective Websockets header or unsupported protocol\n" ); -vnc_client_error (vs); - } - -g_free (protocols); -g_free (version); -g_free (key); -} - -void vncws_encode_frame (QIOBuffer *output,const void *payload, -const size_t payload_size) -{ -size_t header_size =0 ; -unsigned char opcode =WS_OPCODE_BINARY_FRAME ; -union { -char buf[WS_HEAD_MAX_LEN ]; -WsHeader ws; - } header; - -if (!payload_size) { -return ; - } - - header.ws.b0 =0x80 | (opcode &0x0f ); -if (payload_size <=125 ) { - header.ws.b1 = (uint8_t )payload_size; - header_size =2 ; - }else if (payload_size <65536 ) { - header.ws.b1 =0x7e ; - header.ws.u.s16.l16 =cpu_to_be16 ((uint16_t )payload_size); - header_size =4 ; - }else { - header.ws.b1 =0x7f ; - header.ws.u.s64.l64 =cpu_to_be64 (payload_size); - header_size =10 ; - } - -qio_buffer_reserve (output, header_size + payload_size); -qio_buffer_append (output, header.buf, header_size); -qio_buffer_append (output, payload, payload_size); -} - -int vncws_decode_frame_header (QIOBuffer *input, -size_t *header_size, -size_t *payload_remain, -WsMask *payload_mask) -{ -unsigned char opcode =0 , fin =0 , has_mask =0 ; -size_t payload_len; -WsHeader *header = (WsHeader *)input->buffer; - -if (input->offset <WS_HEAD_MIN_LEN +4 ) { -/* header not complete */ -return 0 ; - } - - fin = (header->b0 &0x80 ) >>7 ; - opcode = header->b0 &0x0f ; - has_mask = (header->b1 &0x80 ) >>7 ; - payload_len = header->b1 &0x7f ; - -if (opcode ==WS_OPCODE_CLOSE ) { -/* disconnect */ -return -1 ; - } - -/* Websocket frame sanity check: -* * Websocket fragmentation is not supported. -* * All websockets frames sent by a client have to be masked. -* * Only binary encoding is supported. -*/ -if (!fin || !has_mask || opcode !=WS_OPCODE_BINARY_FRAME ) { -VNC_DEBUG ("Received faulty/unsupported Websocket frame\n" ); -return -2 ; +VNC_DEBUG ("Websocket negotiate starting\n" ); +if (vs->ioc_tag) { +g_source_remove (vs->ioc_tag); + vs->ioc_tag =0 ; } -if (payload_len <126 ) { - *payload_remain = payload_len; - *header_size =6 ; - *payload_mask = header->u.m; - }else if (payload_len ==126 && input->offset >=8 ) { - *payload_remain =be16_to_cpu (header->u.s16.l16); - *header_size =8 ; - *payload_mask = header->u.s16.m16; - }else if (payload_len ==127 && input->offset >=14 ) { - *payload_remain =be64_to_cpu (header->u.s64.l64); - *header_size =14 ; - *payload_mask = header->u.s64.m64; - }else { -/* header not complete */ -return 0 ; - } + wioc =qio_channel_websock_new_server (vs->ioc); -return 1 ; -} - -int vncws_decode_frame_payload (QIOBuffer *input, -size_t *payload_remain,WsMask *payload_mask, -uint8_t **payload,size_t *payload_size) -{ -size_t i; -uint32_t *payload32; - - *payload = input->buffer; -/* If we aren't at the end of the payload, then drop -* off the last bytes, so we're always multiple of 4 -* for purpose of unmasking, except at end of payload -*/ -if (input->offset < *payload_remain) { - *payload_size = input->offset - (input->offset %4 ); - }else { - *payload_size = *payload_remain; - } -if (*payload_size ==0 ) { -return 0 ; - } - *payload_remain -= *payload_size; +object_unref (OBJECT (vs->ioc)); + vs->ioc =QIO_CHANNEL (wioc); -/* unmask frame */ -/* process 1 frame (32 bit op) */ - payload32 = (uint32_t *)(*payload); -for (i =0 ; i < *payload_size /4 ; i++) { - payload32[i] ^= payload_mask->u; - } -/* process the remaining bytes (if any) */ -for (i *=4 ; i < *payload_size; i++) { - (*payload)[i] ^= payload_mask->c[i %4 ]; - } +qio_channel_websock_handshake (wioc, + vncws_handshake_done, + vs, +NULL ); -return 1 ; +return TRUE; }
ui/vnc-ws.h +0 -63
74 lines not on this page · raw message
ui/vnc.c +4 -21
@@ -1123,8 +1123,6 @@ void vnc_disconnect_finish(VncState *vs)qio_buffer_free (&vs->input);qio_buffer_free (&vs->output); -qio_buffer_free (&vs->ws_input); -qio_buffer_free (&vs->ws_output);qapi_free_VncClientInfo (vs->info);
@@ -1277,11 +1275,7 @@ static void vnc_client_write_locked(VncState *vs) } else#endif /* CONFIG_VNC_SASL */ { -if (vs->encode_ws) { -vnc_client_write_ws (vs); - }else { -vnc_client_write_plain (vs); - } +vnc_client_write_plain (vs); } }
@@ -1289,7 +1283,7 @@ static void vnc_client_write(VncState *vs) {vnc_lock_output (vs); -if (vs->output.offset || vs->ws_output.offset) { +if (vs->output.offset) {vnc_client_write_locked (vs); }else if (vs->ioc !=NULL ) {if (vs->ioc_tag) {
@@ -1375,18 +1369,7 @@ static void vnc_client_read(VncState *vs) ret =vnc_client_read_sasl (vs); else#endif /* CONFIG_VNC_SASL */ -if (vs->encode_ws) { - ret =vnc_client_read_ws (vs); -if (ret ==-1 ) { -vnc_disconnect_start (vs); -return ; - }else if (ret ==-2 ) { -vnc_client_error (vs); -return ; - } - }else { - ret =vnc_client_read_plain (vs); - } + ret =vnc_client_read_plain (vs);if (!ret) {if (vs->disconnecting) {vnc_disconnect_finish (vs);
@@ -1476,7 +1459,7 @@ void vnc_write_u8(VncState *vs, uint8_t value)void vnc_flush (VncState *vs) {vnc_lock_output (vs); -if (vs->ioc !=NULL && (vs->output.offset || vs->ws_output.offset)) { +if (vs->ioc !=NULL && vs->output.offset) {vnc_client_write_locked (vs); }vnc_unlock_output (vs);
ui/vnc.h +0 -4
@@ -292,10 +292,6 @@ struct VncStateQIOBuffer output;QIOBuffer input; -QIOBuffer ws_input; -QIOBuffer ws_output; -size_t ws_payload_remain; -WsMask ws_payload_mask;/* current output mode information */ VncWritePixels *write_pixels;PixelFormat client_pf;
Signature · 1 line
2.1.0
RFC PATCH 32/34 char: convert from GIOChannel to QIOChannel
In reply to Daniel P. BerrangeIn preparation for introducing TLS support to the TCP chardev backend, convert existing chardev code from using GIOChannel to QIOChannel. This simplifies the chardev code by removing most of the OS platform conditional code for dealing with file descriptor passing.
--- qemu-char.c | 601 +++++++++++++++++++++++------------------------------------- 1 file changed, 232 insertions(+), 369 deletions(-)
qemu-char.c +232 -369
@@ -31,6 +31,8 @@#include "qapi/qmp-input-visitor.h" #include "qapi/qmp-output-visitor.h" #include "qapi-visit.h" +#include "io/channel-socket.h" +#include "io/channel-file.h" #include <unistd.h> #include <fcntl.h>
@@ -783,7 +785,7 @@ typedef struct IOWatchPoll {GSource parent; -GIOChannel *channel; +QIOChannel *ioc;GSource *src;IOCanReadHandler *fd_can_read;
@@ -806,7 +808,8 @@ static gboolean io_watch_poll_prepare(GSource *source, gint *timeout_) }if (now_active) { - iwp->src =g_io_create_watch (iwp->channel,G_IO_IN |G_IO_ERR |G_IO_HUP ); + iwp->src =qio_channel_create_watch ( + iwp->ioc,G_IO_IN |G_IO_ERR |G_IO_HUP );g_source_set_callback (iwp->src, iwp->fd_read, iwp->opaque,NULL );g_source_attach (iwp->src,NULL ); }else {
@@ -852,9 +855,9 @@ static GSourceFuncs io_watch_poll_funcs = { };/* Can only be used for read */ -static guint io_add_watch_poll (GIOChannel *channel, +static guint io_add_watch_poll (QIOChannel *ioc,IOCanReadHandler *fd_can_read, -GIOFunc fd_read, +QIOChannelFunc fd_read,gpointer user_data) {IOWatchPoll *iwp;
@@ -863,7 +866,7 @@ static guint io_add_watch_poll(GIOChannel *channel, iwp = (IOWatchPoll *)g_source_new (&io_watch_poll_funcs,sizeof (IOWatchPoll)); iwp->fd_can_read = fd_can_read; iwp->opaque = user_data; - iwp->channel = channel; + iwp->ioc = ioc; iwp->fd_read = (GSourceFunc ) fd_read; iwp->src =NULL ;
@@ -899,79 +902,53 @@ static void remove_fd_in_watch(CharDriverState *chr) } } -#ifndef _WIN32 -static GIOChannel *io_channel_from_fd (int fd) -{ -GIOChannel *chan; - -if (fd ==-1 ) { -return NULL ; - } - - chan =g_io_channel_unix_new (fd); - -g_io_channel_set_encoding (chan,NULL ,NULL ); -g_io_channel_set_buffered (chan, FALSE); - -return chan; -} -#endif -static GIOChannel *io_channel_from_socket (int fd) +static int io_channel_send_full (QIOChannel *ioc, +const void *buf,size_t len, +int *fds,size_t nfds) { -GIOChannel *chan; +size_t offset =0 ; -if (fd ==-1 ) { -return NULL ; - } +while (offset < len) { +ssize_t ret =0 ; +struct iovec iov = { .iov_base = (char *)buf + offset, + .iov_len = len - offset }; + + ret =qio_channel_writev_full ( + ioc, &iov,1 , + fds, nfds,0 ,NULL ); +if (ret ==QIO_CHANNEL_ERR_BLOCK ) { + errno =EAGAIN ; +return -1 ; + } -#ifdef _WIN32 - chan =g_io_channel_win32_new_socket (fd); -#else - chan =g_io_channel_unix_new (fd); -#endif +if (ret >0 ) { + offset += ret; + } +if (ret <0 ) { +if (offset) { +return offset; + } -g_io_channel_set_encoding (chan,NULL ,NULL ); -g_io_channel_set_buffered (chan, FALSE); + errno =EINVAL ; +return -1 ; + } + } -return chan; +return offset; } -static int io_channel_send (GIOChannel *fd,const void *buf,size_t len) -{ -size_t offset =0 ; -GIOStatus status =G_IO_STATUS_NORMAL ; -while (offset < len && status ==G_IO_STATUS_NORMAL ) { -gsize bytes_written =0 ; - - status =g_io_channel_write_chars (fd, buf + offset, len - offset, - &bytes_written,NULL ); - offset += bytes_written; - } - -if (offset >0 ) { -return offset; - } -switch (status) { -case G_IO_STATUS_NORMAL : -g_assert (len ==0 ); -return 0 ; -case G_IO_STATUS_AGAIN : - errno =EAGAIN ; -return -1 ; -default : -break ; - } - errno =EINVAL ; -return -1 ; +static int io_channel_send (QIOChannel *ioc,const void *buf,size_t len) +{ +return io_channel_send_full (ioc, buf, len,NULL ,0 ); }#ifndef _WIN32typedef struct FDCharDriver {CharDriverState *chr; -GIOChannel *fd_in, *fd_out; +QIOChannel *ioc_in, *ioc_out;int max_size;QTAILQ_ENTRY (FDCharDriver) node; }FDCharDriver ;
@@ -981,17 +958,16 @@ static int fd_chr_write(CharDriverState *chr, const uint8_t *buf, int len) {FDCharDriver *s = chr->opaque; -return io_channel_send (s->fd_out, buf, len); +return io_channel_send (s->ioc_out, buf, len); } -static gboolean fd_chr_read (GIOChannel *chan,GIOCondition cond,void *opaque) +static gboolean fd_chr_read (QIOChannel *chan,GIOCondition cond,void *opaque) {CharDriverState *chr = opaque;FDCharDriver *s = chr->opaque;int len;uint8_t buf[READ_BUF_LEN ]; -GIOStatus status; -gsize bytes_read; +ssize_t ret; len =sizeof (buf);if (len > s->max_size) {
@@ -1001,15 +977,15 @@ static gboolean fd_chr_read(GIOChannel *chan, GIOCondition cond, void *opaque)return TRUE; } - status =g_io_channel_read_chars (chan, (gchar *)buf, - len, &bytes_read,NULL ); -if (status ==G_IO_STATUS_EOF ) { + ret =qio_channel_read ( + chan, (gchar *)buf, len,NULL ); +if (ret ==0 ) {remove_fd_in_watch (chr);qemu_chr_be_event (chr,CHR_EVENT_CLOSED );return FALSE; } -if (status ==G_IO_STATUS_NORMAL ) { -qemu_chr_be_write (chr, buf, bytes_read); +if (ret >0 ) { +qemu_chr_be_write (chr, buf, ret); }return TRUE;
@@ -1027,7 +1003,7 @@ static int fd_chr_read_poll(void *opaque)static GSource *fd_chr_add_watch (CharDriverState *chr,GIOCondition cond) {FDCharDriver *s = chr->opaque; -return g_io_create_watch (s->fd_out, cond); +return qio_channel_create_watch (s->ioc_out, cond); }static void fd_chr_update_read_handler (CharDriverState *chr)
@@ -1035,8 +1011,9 @@ static void fd_chr_update_read_handler(CharDriverState *chr)FDCharDriver *s = chr->opaque;remove_fd_in_watch (chr); -if (s->fd_in) { - chr->fd_in_tag =io_add_watch_poll (s->fd_in, fd_chr_read_poll, +if (s->ioc_in) { + chr->fd_in_tag =io_add_watch_poll (s->ioc_in, + fd_chr_read_poll, fd_chr_read, chr); } }
@@ -1046,11 +1023,11 @@ static void fd_chr_close(struct CharDriverState *chr)FDCharDriver *s = chr->opaque;remove_fd_in_watch (chr); -if (s->fd_in) { -g_io_channel_unref (s->fd_in); +if (s->ioc_in) { +object_unref (OBJECT (s->ioc_in)); } -if (s->fd_out) { -g_io_channel_unref (s->fd_out); +if (s->ioc_out) { +object_unref (OBJECT (s->ioc_out)); }g_free (s);
@@ -1065,8 +1042,8 @@ static CharDriverState *qemu_chr_open_fd(int fd_in, int fd_out) chr =qemu_chr_alloc (); s =g_malloc0 (sizeof (FDCharDriver)); - s->fd_in =io_channel_from_fd (fd_in); - s->fd_out =io_channel_from_fd (fd_out); + s->ioc_in =QIO_CHANNEL (qio_channel_file_new_fd (fd_in)); + s->ioc_out =QIO_CHANNEL (qio_channel_file_new_fd (fd_out));qemu_set_nonblock (fd_out); s->chr = chr; chr->opaque = s;
@@ -1199,7 +1176,7 @@ static CharDriverState *qemu_chr_open_stdio(ChardevStdio *opts)#define HAVE_CHARDEV_TTY 1 typedef struct { - GIOChannel *fd; + QIOChannel *ioc; int read_bytes; /* Protected by the CharDriverState chr_write_lock. */
@@ -1249,8 +1226,9 @@ static void pty_chr_update_read_handler_locked(CharDriverState *chr) {PtyCharDriver *s = chr->opaque;GPollFD pfd; +QIOChannelFile *fioc =QIO_CHANNEL_FILE (s->ioc); - pfd.fd =g_io_channel_unix_get_fd (s->fd); + pfd.fd = fioc->fd; pfd.events =G_IO_OUT ; pfd.revents =0 ;g_poll (&pfd,1 ,0 );
@@ -1280,7 +1258,7 @@ static int pty_chr_write(CharDriverState *chr, const uint8_t *buf, int len)return 0 ; } } -return io_channel_send (s->fd, buf, len); +return io_channel_send (s->ioc, buf, len); }static GSource *pty_chr_add_watch (CharDriverState *chr,GIOCondition cond)
@@ -1289,7 +1267,7 @@ static GSource *pty_chr_add_watch(CharDriverState *chr, GIOCondition cond)if (!s->connected) {return NULL ; } -return g_io_create_watch (s->fd, cond); +return qio_channel_create_watch (s->ioc, cond); }static int pty_chr_read_poll (void *opaque)
@@ -1301,13 +1279,13 @@ static int pty_chr_read_poll(void *opaque)return s->read_bytes; } -static gboolean pty_chr_read (GIOChannel *chan,GIOCondition cond,void *opaque) +static gboolean pty_chr_read (QIOChannel *chan,GIOCondition cond,void *opaque) {CharDriverState *chr = opaque;PtyCharDriver *s = chr->opaque; -gsize size, len; +gsize len;uint8_t buf[READ_BUF_LEN ]; -GIOStatus status; +ssize_t ret; len =sizeof (buf);if (len > s->read_bytes)
@@ -1315,13 +1293,13 @@ static gboolean pty_chr_read(GIOChannel *chan, GIOCondition cond, void *opaque)if (len ==0 ) {return TRUE; } - status =g_io_channel_read_chars (s->fd, (gchar *)buf, len, &size,NULL ); -if (status !=G_IO_STATUS_NORMAL ) { + ret =qio_channel_read (s->ioc, (char *)buf, len,NULL ); +if (ret <=0 ) {pty_chr_state (chr,0 );return FALSE; }else {pty_chr_state (chr,1 ); -qemu_chr_be_write (chr, buf, size); +qemu_chr_be_write (chr, buf, ret); }return TRUE; }
@@ -1363,7 +1341,8 @@ static void pty_chr_state(CharDriverState *chr, int connected) s->open_tag =g_idle_add (qemu_chr_be_generic_open_func, chr); }if (!chr->fd_in_tag) { - chr->fd_in_tag =io_add_watch_poll (s->fd, pty_chr_read_poll, + chr->fd_in_tag =io_add_watch_poll (s->ioc, + pty_chr_read_poll, pty_chr_read, chr); } }
@@ -1372,13 +1351,10 @@ static void pty_chr_state(CharDriverState *chr, int connected)static void pty_chr_close (struct CharDriverState *chr) {PtyCharDriver *s = chr->opaque; -int fd;qemu_mutex_lock (&chr->chr_write_lock);pty_chr_state (chr,0 ); - fd =g_io_channel_unix_get_fd (s->fd); -g_io_channel_unref (s->fd); -close (fd); +object_unref (OBJECT (s->ioc));if (s->timer_tag) {g_source_remove (s->timer_tag); s->timer_tag =0 ;
@@ -1421,7 +1397,7 @@ static CharDriverState *qemu_chr_open_pty(const char *id, chr->chr_add_watch = pty_chr_add_watch; chr->explicit_be_open = true; - s->fd =io_channel_from_fd (master_fd); + s->ioc =QIO_CHANNEL (qio_channel_file_new_fd (master_fd)); s->timer_tag =0 ;return chr;
@@ -1545,12 +1521,13 @@ static void tty_serial_init(int fd, int speed,static int tty_serial_ioctl (CharDriverState *chr,int cmd,void *arg) {FDCharDriver *s = chr->opaque; +QIOChannelFile *fioc =QIO_CHANNEL_FILE (s->ioc_in);switch (cmd) {case CHR_IOCTL_SERIAL_SET_PARAMS : {QEMUSerialSetParams *ssp = arg; -tty_serial_init (g_io_channel_unix_get_fd (s->fd_in), +tty_serial_init (fioc->fd, ssp->speed, ssp->parity, ssp->data_bits, ssp->stop_bits); }
@@ -1559,7 +1536,7 @@ static int tty_serial_ioctl(CharDriverState *chr, int cmd, void *arg) {int enable = *(int *)arg;if (enable) { -tcsendbreak (g_io_channel_unix_get_fd (s->fd_in),1 ); +tcsendbreak (fioc->fd,1 ); } }break ;
@@ -1567,7 +1544,7 @@ static int tty_serial_ioctl(CharDriverState *chr, int cmd, void *arg) {int sarg =0 ;int *targ = (int *)arg; -ioctl (g_io_channel_unix_get_fd (s->fd_in),TIOCMGET , &sarg); +ioctl (fioc->fd,TIOCMGET , &sarg); *targ =0 ;if (sarg &TIOCM_CTS ) *targ |=CHR_TIOCM_CTS ;
@@ -1587,7 +1564,7 @@ static int tty_serial_ioctl(CharDriverState *chr, int cmd, void *arg) {int sarg = *(int *)arg;int targ =0 ; -ioctl (g_io_channel_unix_get_fd (s->fd_in),TIOCMGET , &targ); +ioctl (fioc->fd,TIOCMGET , &targ); targ &= ~(CHR_TIOCM_CTS |CHR_TIOCM_CAR |CHR_TIOCM_DSR |CHR_TIOCM_RI |CHR_TIOCM_DTR |CHR_TIOCM_RTS );if (sarg &CHR_TIOCM_CTS )
@@ -1602,7 +1579,7 @@ static int tty_serial_ioctl(CharDriverState *chr, int cmd, void *arg) targ |=TIOCM_DTR ;if (sarg &CHR_TIOCM_RTS ) targ |=TIOCM_RTS ; -ioctl (g_io_channel_unix_get_fd (s->fd_in),TIOCMSET , &targ); +ioctl (fioc->fd,TIOCMSET , &targ); }break ;default :
@@ -1613,18 +1590,7 @@ static int tty_serial_ioctl(CharDriverState *chr, int cmd, void *arg)static void qemu_chr_close_tty (CharDriverState *chr) { -FDCharDriver *s = chr->opaque; -int fd =-1 ; - -if (s) { - fd =g_io_channel_unix_get_fd (s->fd_in); - } -fd_chr_close (chr); - -if (fd >=0 ) { -close (fd); - } }static CharDriverState *qemu_chr_open_tty_fd (int fd)
@@ -2386,7 +2352,7 @@ static CharDriverState *qemu_chr_open_stdio(ChardevStdio *opts) typedef struct { int fd; - GIOChannel *chan; + QIOChannel *ioc; uint8_t buf[READ_BUF_LEN]; int bufcnt; int bufptr;
@@ -2397,17 +2363,9 @@ typedef struct {static int udp_chr_write (CharDriverState *chr,const uint8_t *buf,int len) {NetCharDriver *s = chr->opaque; -gsize bytes_written; -GIOStatus status; - - status =g_io_channel_write_chars (s->chan, (const gchar *)buf, len, &bytes_written,NULL ); -if (status ==G_IO_STATUS_EOF ) { -return 0 ; - }else if (status !=G_IO_STATUS_NORMAL ) { -return -1 ; - } -return bytes_written; +return qio_channel_write ( + s->ioc, (const char *)buf, len,NULL ); }static int udp_chr_read_poll (void *opaque)
@@ -2428,24 +2386,22 @@ static int udp_chr_read_poll(void *opaque)return s->max_size; } -static gboolean udp_chr_read (GIOChannel *chan,GIOCondition cond,void *opaque) +static gboolean udp_chr_read (QIOChannel *chan,GIOCondition cond,void *opaque) {CharDriverState *chr = opaque;NetCharDriver *s = chr->opaque; -gsize bytes_read =0 ; -GIOStatus status; +ssize_t ret;if (s->max_size ==0 ) {return TRUE; } - status =g_io_channel_read_chars (s->chan, (gchar *)s->buf,sizeof (s->buf), - &bytes_read,NULL ); - s->bufcnt = bytes_read; - s->bufptr = s->bufcnt; -if (status !=G_IO_STATUS_NORMAL ) { + ret =qio_channel_read ( + s->ioc, (char *)s->buf,sizeof (s->buf),NULL ); +if (ret <=0 ) {remove_fd_in_watch (chr);return FALSE; } + s->bufcnt = ret; s->bufptr =0 ;while (s->max_size >0 && s->bufptr < s->bufcnt) {
@@ -2462,8 +2418,9 @@ static void udp_chr_update_read_handler(CharDriverState *chr)NetCharDriver *s = chr->opaque;remove_fd_in_watch (chr); -if (s->chan) { - chr->fd_in_tag =io_add_watch_poll (s->chan, udp_chr_read_poll, +if (s->ioc) { + chr->fd_in_tag =io_add_watch_poll (s->ioc, + udp_chr_read_poll, udp_chr_read, chr); } }
@@ -2473,9 +2430,8 @@ static void udp_chr_close(CharDriverState *chr)NetCharDriver *s = chr->opaque;remove_fd_in_watch (chr); -if (s->chan) { -g_io_channel_unref (s->chan); -closesocket (s->fd); +if (s->ioc) { +object_unref (OBJECT (s->ioc)); }g_free (s);qemu_chr_be_event (chr,CHR_EVENT_CLOSED );
@@ -2490,7 +2446,11 @@ static CharDriverState *qemu_chr_open_udp_fd(int fd) s =g_malloc0 (sizeof (NetCharDriver)); s->fd = fd; - s->chan =io_channel_from_socket (s->fd); + s->ioc =QIO_CHANNEL (qio_channel_socket_new_fd (fd,NULL )); +if (!s->ioc) { +g_free (s); +return NULL ; + } s->bufcnt =0 ; s->bufptr =0 ; chr->opaque = s;
@@ -2506,19 +2466,18 @@ static CharDriverState *qemu_chr_open_udp_fd(int fd)/* TCP Net console */ typedef struct { - - GIOChannel *chan, *listen_chan; + QIOChannel *ioc; + QIOChannelSocket *listen_ioc; guint listen_tag; - int fd, listen_fd; int connected; int max_size; int do_telnetopt; int do_nodelay; int is_unix; int *read_msgfds; - int read_msgfds_num; + size_t read_msgfds_num; int *write_msgfds; - int write_msgfds_num; + size_t write_msgfds_num; SocketAddress *addr; bool is_listen;
@@ -2552,68 +2511,25 @@ static void check_report_connect_error(CharDriverState *chr,qemu_chr_socket_restart_timer (chr); } -static gboolean tcp_chr_accept (GIOChannel *chan,GIOCondition cond,void *opaque); - -#ifndef _WIN32 -static int unix_send_msgfds (CharDriverState *chr,const uint8_t *buf,int len) -{ -TCPCharDriver *s = chr->opaque; -struct msghdr msgh; -struct iovec iov; -int r; - -size_t fd_size = s->write_msgfds_num *sizeof (int ); -char control[CMSG_SPACE (fd_size)]; -struct cmsghdr *cmsg; - -memset (&msgh,0 ,sizeof (msgh)); -memset (control,0 ,sizeof (control)); - -/* set the payload */ - iov.iov_base = (uint8_t *) buf; - iov.iov_len = len; - - msgh.msg_iov = &iov; - msgh.msg_iovlen =1 ; - - msgh.msg_control = control; - msgh.msg_controllen =sizeof (control); - - cmsg =CMSG_FIRSTHDR (&msgh); - - cmsg->cmsg_len =CMSG_LEN (fd_size); - cmsg->cmsg_level =SOL_SOCKET ; - cmsg->cmsg_type =SCM_RIGHTS ; -memcpy (CMSG_DATA (cmsg), s->write_msgfds, fd_size); - -do { - r =sendmsg (s->fd, &msgh,0 ); - }while (r <0 && errno ==EINTR ); - -/* free the written msgfds, no matter what */ -if (s->write_msgfds_num) { -g_free (s->write_msgfds); - s->write_msgfds =0 ; - s->write_msgfds_num =0 ; - } - -return r; -} -#endif +static gboolean tcp_chr_accept (QIOChannel *chan, +GIOCondition cond, +void *opaque);/* Called with chr_write_lock held. */ static int tcp_chr_write (CharDriverState *chr,const uint8_t *buf,int len) {TCPCharDriver *s = chr->opaque;if (s->connected) { -#ifndef _WIN32 -if (s->is_unix && s->write_msgfds_num) { -return unix_send_msgfds (chr, buf, len); - } else -#endif - { -return io_channel_send (s->chan, buf, len); +int ret =io_channel_send (s->ioc, buf, len); + +/* free the written msgfds, no matter what */ +if (s->write_msgfds_num) { +g_free (s->write_msgfds); + s->write_msgfds =0 ; + s->write_msgfds_num =0 ; } + +return ret; }else {/* XXX: indicate an error ? */ return len;
@@ -2724,98 +2640,53 @@ static int tcp_set_msgfds(CharDriverState *chr, int *fds, int num)return 0 ; } -#ifndef _WIN32 -static void unix_process_msgfd (CharDriverState *chr,struct msghdr *msg) +static ssize_t tcp_chr_recv (CharDriverState *chr,char *buf,size_t len) {TCPCharDriver *s = chr->opaque; -struct cmsghdr *cmsg; +struct iovec iov = { .iov_base = buf, .iov_len = len }; +int ret; +size_t i; -for (cmsg =CMSG_FIRSTHDR (msg); cmsg; cmsg =CMSG_NXTHDR (msg, cmsg)) { -int fd_size, i; +/* close and clean read_msgfds */ +for (i =0 ; i < s->read_msgfds_num; i++) { +close (s->read_msgfds[i]); + } -if (cmsg->cmsg_len <CMSG_LEN (sizeof (int )) || - cmsg->cmsg_level !=SOL_SOCKET || - cmsg->cmsg_type !=SCM_RIGHTS ) { -continue ; - } +if (s->read_msgfds_num) { +g_free (s->read_msgfds); + } - fd_size = cmsg->cmsg_len -CMSG_LEN (0 ); +if (qio_channel_has_feature (s->ioc,QIO_CHANNEL_FEATURE_FD_PASS )) { + ret =qio_channel_readv_full (s->ioc, &iov,1 , + &s->read_msgfds, &s->read_msgfds_num, +0 ,NULL ); + }else { + ret =qio_channel_readv_full (s->ioc, &iov,1 , +NULL ,NULL , +0 ,NULL ); + } -if (!fd_size) { +for (i =0 ; i < s->read_msgfds_num; i++) { +int fd = s->read_msgfds[i]; +if (fd <0 ) {continue ; } -/* close and clean read_msgfds */ -for (i =0 ; i < s->read_msgfds_num; i++) { -close (s->read_msgfds[i]); - } - -if (s->read_msgfds_num) { -g_free (s->read_msgfds); - } - - s->read_msgfds_num = fd_size /sizeof (int ); - s->read_msgfds =g_malloc (fd_size); -memcpy (s->read_msgfds,CMSG_DATA (cmsg), fd_size); - -for (i =0 ; i < s->read_msgfds_num; i++) { -int fd = s->read_msgfds[i]; -if (fd <0 ) { -continue ; - } - -/* O_NONBLOCK is preserved across SCM_RIGHTS so reset it */ -qemu_set_block (fd); +/* O_NONBLOCK is preserved across SCM_RIGHTS so reset it */ +qemu_set_block (fd); -#ifndef MSG_CMSG_CLOEXEC -qemu_set_cloexec (fd); -#endif - } - } -} - -static ssize_t tcp_chr_recv (CharDriverState *chr,char *buf,size_t len) -{ -TCPCharDriver *s = chr->opaque; -struct msghdr msg = {NULL , }; -struct iovec iov[1 ]; -union { -struct cmsghdr cmsg; -char control[CMSG_SPACE (sizeof (int ) *TCP_MAX_FDS )]; - } msg_control; -int flags =0 ; -ssize_t ret; - - iov[0 ].iov_base = buf; - iov[0 ].iov_len = len; - - msg.msg_iov = iov; - msg.msg_iovlen =1 ; - msg.msg_control = &msg_control; - msg.msg_controllen =sizeof (msg_control); - -#ifdef MSG_CMSG_CLOEXEC - flags |=MSG_CMSG_CLOEXEC ; +#ifndef MSG_CMSG_CLOEXEC +qemu_set_cloexec (fd);#endif - ret =recvmsg (s->fd, &msg, flags); -if (ret >0 && s->is_unix) { -unix_process_msgfd (chr, &msg); }return ret; } -#else -static ssize_t tcp_chr_recv (CharDriverState *chr,char *buf,size_t len) -{ -TCPCharDriver *s = chr->opaque; -return qemu_recv (s->fd, buf, len,0 ); -} -#endif static GSource *tcp_chr_add_watch (CharDriverState *chr,GIOCondition cond) {TCPCharDriver *s = chr->opaque; -return g_io_create_watch (s->chan, cond); +return qio_channel_create_watch (s->ioc, cond); }static void tcp_chr_disconnect (CharDriverState *chr)
@@ -2823,15 +2694,13 @@ static void tcp_chr_disconnect(CharDriverState *chr)TCPCharDriver *s = chr->opaque; s->connected =0 ; -if (s->listen_chan) { - s->listen_tag =g_io_add_watch (s->listen_chan,G_IO_IN , - tcp_chr_accept, chr); +if (s->listen_ioc) { + s->listen_tag =qio_channel_add_watch ( +QIO_CHANNEL (s->listen_ioc),G_IO_IN , tcp_chr_accept, chr); }remove_fd_in_watch (chr); -g_io_channel_unref (s->chan); - s->chan =NULL ; -closesocket (s->fd); - s->fd =-1 ; +object_unref (OBJECT (s->ioc)); + s->ioc =NULL ;SocketAddress_to_str (chr->filename,CHR_MAX_FILENAME_SIZE ,"disconnected:" , s->addr, s->is_listen, s->is_telnet);qemu_chr_be_event (chr,CHR_EVENT_CLOSED );
@@ -2840,7 +2709,7 @@ static void tcp_chr_disconnect(CharDriverState *chr) } } -static gboolean tcp_chr_read (GIOChannel *chan,GIOCondition cond,void *opaque) +static gboolean tcp_chr_read (QIOChannel *chan,GIOCondition cond,void *opaque) {CharDriverState *chr = opaque;TCPCharDriver *s = chr->opaque;
@@ -2860,7 +2729,7 @@ static gboolean tcp_chr_read(GIOChannel *chan, GIOCondition cond, void *opaque)if (len > s->max_size) len = s->max_size; size =tcp_chr_recv (chr, (void *)buf, len); -if (size ==0 ) { +if (size ==0 || size ==-1 ) {/* connection closed */ tcp_chr_disconnect (chr); }else if (size >0 ) {
@@ -2908,25 +2777,17 @@ static void tcp_chr_connect(void *opaque) {CharDriverState *chr = opaque;TCPCharDriver *s = chr->opaque; -struct sockaddr_storage ss, ps; -socklen_t ss_len =sizeof (ss), ps_len =sizeof (ps); - -memset (&ss,0 , ss_len); -if (getsockname (s->fd, (struct sockaddr *) &ss, &ss_len) !=0 ) { -snprintf (chr->filename,CHR_MAX_FILENAME_SIZE , -"Error in getsockname: %s\n" ,strerror (errno)); - }else if (getpeername (s->fd, (struct sockaddr *) &ps, &ps_len) !=0 ) { -snprintf (chr->filename,CHR_MAX_FILENAME_SIZE , -"Error in getpeername: %s\n" ,strerror (errno)); - }else { -sockaddr_to_str (chr->filename,CHR_MAX_FILENAME_SIZE , - &ss, ss_len, &ps, ps_len, - s->is_listen, s->is_telnet); - } +QIOChannelSocket *sioc =QIO_CHANNEL_SOCKET (s->ioc); + +sockaddr_to_str (chr->filename,CHR_MAX_FILENAME_SIZE , + &sioc->localAddr, sioc->localAddrLen, + &sioc->remoteAddr, sioc->remoteAddrLen, + s->is_listen, s->is_telnet); s->connected =1 ; -if (s->chan) { - chr->fd_in_tag =io_add_watch_poll (s->chan, tcp_chr_read_poll, +if (s->ioc) { + chr->fd_in_tag =io_add_watch_poll (s->ioc, + tcp_chr_read_poll, tcp_chr_read, chr); }qemu_chr_be_generic_open (chr);
@@ -2937,38 +2798,42 @@ static void tcp_chr_update_read_handler(CharDriverState *chr)TCPCharDriver *s = chr->opaque;remove_fd_in_watch (chr); -if (s->chan) { - chr->fd_in_tag =io_add_watch_poll (s->chan, tcp_chr_read_poll, +if (s->ioc) { + chr->fd_in_tag =io_add_watch_poll (s->ioc, + tcp_chr_read_poll, tcp_chr_read, chr); } }#define IACSET (x,a,b,c) x[0] = a; x[1] = b; x[2] = c; -static void tcp_chr_telnet_init (int fd) +static void tcp_chr_telnet_init (QIOChannel *ioc) {char buf[3 ];/* Send the telnet negotion to put telnet in binary, no echo, single char mode */ IACSET (buf,0xff ,0xfb ,0x01 );/* IAC WILL ECHO */ -send (fd, (char *)buf,3 ,0 ); +qio_channel_write (ioc, buf,3 ,NULL );IACSET (buf,0xff ,0xfb ,0x03 );/* IAC WILL Suppress go ahead */ -send (fd, (char *)buf,3 ,0 ); +qio_channel_write (ioc, buf,3 ,NULL );IACSET (buf,0xff ,0xfb ,0x00 );/* IAC WILL Binary */ -send (fd, (char *)buf,3 ,0 ); +qio_channel_write (ioc, buf,3 ,NULL );IACSET (buf,0xff ,0xfd ,0x00 );/* IAC DO Binary */ -send (fd, (char *)buf,3 ,0 ); +qio_channel_write (ioc, buf,3 ,NULL ); } -static int tcp_chr_add_client (CharDriverState *chr,int fd) +static int tcp_chr_new_client (CharDriverState *chr,QIOChannel *ioc) {TCPCharDriver *s = chr->opaque; -if (s->fd !=-1 ) +if (s->ioc !=NULL ) {return -1 ; + } -qemu_set_nonblock (fd); -if (s->do_nodelay) -socket_set_nodelay (fd); - s->fd = fd; - s->chan =io_channel_from_socket (fd); + s->ioc = ioc; +object_ref (OBJECT (ioc)); + +qio_channel_set_blocking (s->ioc, false); +if (s->do_nodelay) { +qio_channel_socket_set_nodelay (QIO_CHANNEL_SOCKET (s->ioc), true); + }if (s->listen_tag) {g_source_remove (s->listen_tag); s->listen_tag =0 ;
@@ -2978,41 +2843,42 @@ static int tcp_chr_add_client(CharDriverState *chr, int fd)return 0 ; } -static gboolean tcp_chr_accept (GIOChannel *channel,GIOCondition cond,void *opaque) + +static int tcp_chr_add_client (CharDriverState *chr,int fd) +{ +int ret; +QIOChannel *ioc; + + ioc =QIO_CHANNEL (qio_channel_socket_new_fd (fd,NULL )); +if (!ioc) { +return -1 ; + } + ret =tcp_chr_new_client (chr, ioc); +object_unref (OBJECT (ioc)); +return ret; +} + +static gboolean tcp_chr_accept (QIOChannel *channel, +GIOCondition cond, +void *opaque) {CharDriverState *chr = opaque;TCPCharDriver *s = chr->opaque; -struct sockaddr_in saddr; -#ifndef _WIN32 -struct sockaddr_un uaddr; -#endif -struct sockaddr *addr; -socklen_t len; -int fd; +QIOChannel *ioc; -for (;;) { -#ifndef _WIN32 -if (s->is_unix) { - len =sizeof (uaddr); - addr = (struct sockaddr *)&uaddr; - } else -#endif - { - len =sizeof (saddr); - addr = (struct sockaddr *)&saddr; - } - fd =qemu_accept (s->listen_fd, addr, &len); -if (fd <0 && errno !=EINTR ) { - s->listen_tag =0 ; -return FALSE; - }else if (fd >=0 ) { -if (s->do_telnetopt) -tcp_chr_telnet_init (fd); -break ; - } + ioc =QIO_CHANNEL ( +qio_channel_socket_accept (QIO_CHANNEL_SOCKET (channel),NULL )); +if (!ioc) { +return TRUE; + } + +if (s->do_telnetopt) { +tcp_chr_telnet_init (ioc); } -if (tcp_chr_add_client (chr, fd) <0 ) -close (fd); + +tcp_chr_new_client (chr, ioc); + +object_unref (OBJECT (ioc));return TRUE; }
@@ -3027,22 +2893,16 @@ static void tcp_chr_close(CharDriverState *chr) s->reconnect_timer =0 ; }qapi_free_SocketAddress (s->addr); -if (s->fd >=0 ) { -remove_fd_in_watch (chr); -if (s->chan) { -g_io_channel_unref (s->chan); - } -closesocket (s->fd); +remove_fd_in_watch (chr); +if (s->ioc) { +object_unref (OBJECT (s->ioc)); } -if (s->listen_fd >=0 ) { -if (s->listen_tag) { -g_source_remove (s->listen_tag); - s->listen_tag =0 ; - } -if (s->listen_chan) { -g_io_channel_unref (s->listen_chan); - } -closesocket (s->listen_fd); +if (s->listen_tag) { +g_source_remove (s->listen_tag); + s->listen_tag =0 ; + } +if (s->listen_ioc) { +object_unref (OBJECT (s->listen_ioc)); }if (s->read_msgfds_num) {for (i =0 ; i < s->read_msgfds_num; i++) {
@@ -3062,16 +2922,21 @@ static void qemu_chr_finish_socket_connection(CharDriverState *chr, int fd)TCPCharDriver *s = chr->opaque;if (s->is_listen) { - s->listen_fd = fd; - s->listen_chan =io_channel_from_socket (s->listen_fd); - s->listen_tag =g_io_add_watch (s->listen_chan,G_IO_IN , - tcp_chr_accept, chr); + s->listen_ioc =qio_channel_socket_new_fd (fd,NULL ); +if (!s->listen_ioc) { +close (fd); +return ; + } + s->listen_tag =qio_channel_add_watch ( +QIO_CHANNEL (s->listen_ioc),G_IO_IN , tcp_chr_accept, chr); }else { - s->connected =1 ; - s->fd = fd; -socket_set_nodelay (fd); - s->chan =io_channel_from_socket (s->fd); -tcp_chr_connect (chr); +QIOChannel *ioc =QIO_CHANNEL (qio_channel_socket_new_fd (fd,NULL )); +if (!ioc) { +close (fd); +return ; + } +tcp_chr_new_client (chr, ioc); +object_unref (OBJECT (ioc)); } }
@@ -4143,8 +4008,6 @@ static CharDriverState *qmp_chardev_open_socket(ChardevSocket *sock, chr =qemu_chr_alloc (); s =g_malloc0 (sizeof (TCPCharDriver)); - s->fd =-1 ; - s->listen_fd =-1 ; s->is_unix = addr->kind ==SOCKET_ADDRESS_KIND_UNIX ; s->is_listen = is_listen; s->is_telnet = is_telnet;
@@ -4187,8 +4050,8 @@ static CharDriverState *qmp_chardev_open_socket(ChardevSocket *sock,if (is_listen && is_waitconnect) {fprintf (stderr,"QEMU waiting for connection on: %s\n" , chr->filename); -tcp_chr_accept (s->listen_chan,G_IO_IN , chr); -qemu_set_nonblock (s->listen_fd); +tcp_chr_accept (QIO_CHANNEL (s->listen_ioc),G_IO_IN , chr); +qio_channel_set_blocking (QIO_CHANNEL (s->listen_ioc), false); }return chr;
Signature · 1 line
2.1.0
RFC PATCH 33/34 char: don't assume telnet initialization will not block
In reply to Daniel P. BerrangeThe current code for doing telnet initialization is writing to a socket without checking the return status. While it is highly unlikely to be a problem when writing to a bare socket, as the buffers are large enough to prevent blocking, this cannot be assumed safe with TLS sockets. So write the telnet initialization code into a memory buffer and then use an I/O watch to fully send the data.
--- qemu-char.c | 85 ++++++++++++++++++++++++++++++++++++++++++++++++------------- 1 file changed, 67 insertions(+), 18 deletions(-)
qemu-char.c +67 -18
@@ -2805,19 +2805,68 @@ static void tcp_chr_update_read_handler(CharDriverState *chr) } } -#define IACSET (x,a,b,c) x[0] = a; x[1] = b; x[2] = c; -static void tcp_chr_telnet_init (QIOChannel *ioc) +typedef struct { +CharDriverState *chr; +char buf[12 ]; +size_t buflen; +}TCPCharDriverTelnetInit ; + +static gboolean tcp_chr_telnet_init_io (QIOChannel *ioc, + GIOCondition cond G_GNUC_UNUSED, +gpointer user_data) { -char buf[3 ]; -/* Send the telnet negotion to put telnet in binary, no echo, single char mode */ -IACSET (buf,0xff ,0xfb ,0x01 );/* IAC WILL ECHO */ -qio_channel_write (ioc, buf,3 ,NULL ); -IACSET (buf,0xff ,0xfb ,0x03 );/* IAC WILL Suppress go ahead */ -qio_channel_write (ioc, buf,3 ,NULL ); -IACSET (buf,0xff ,0xfb ,0x00 );/* IAC WILL Binary */ -qio_channel_write (ioc, buf,3 ,NULL ); -IACSET (buf,0xff ,0xfd ,0x00 );/* IAC DO Binary */ -qio_channel_write (ioc, buf,3 ,NULL ); +TCPCharDriverTelnetInit *init = user_data; +ssize_t ret; + + ret =qio_channel_write (ioc, init->buf, init->buflen,NULL ); +if (ret <0 ) { +if (ret ==QIO_CHANNEL_ERR_BLOCK ) { + ret =0 ; + }else { +tcp_chr_disconnect (init->chr); +return FALSE; + } + } + init->buflen -= ret; + +if (init->buflen ==0 ) { +tcp_chr_connect (init->chr); +return FALSE; + } + +return TRUE; +} + +static void tcp_chr_telnet_init (CharDriverState *chr) +{ +TCPCharDriver *s = chr->opaque; +TCPCharDriverTelnetInit *init = +g_new0 (TCPCharDriverTelnetInit,1 ); +size_t n =0 ; + + init->chr = chr; + init->buflen =12 ; + +#define IACSET (x, a, b, c) \ + do { \ + x[n++] = a; \ + x[n++] = b; \ + x[n++] = c; \ + } while (0) + +/* Prep the telnet negotion to put telnet in binary, +* no echo, single char mode */ +IACSET (init->buf,0xff ,0xfb ,0x01 );/* IAC WILL ECHO */ +IACSET (init->buf,0xff ,0xfb ,0x03 );/* IAC WILL Suppress go ahead */ +IACSET (init->buf,0xff ,0xfb ,0x00 );/* IAC WILL Binary */ +IACSET (init->buf,0xff ,0xfd ,0x00 );/* IAC DO Binary */ + +#undef IACSET + +qio_channel_add_watch ( + s->ioc,G_IO_OUT , + tcp_chr_telnet_init_io, + init); }static int tcp_chr_new_client (CharDriverState *chr,QIOChannel *ioc)
@@ -2838,7 +2887,12 @@ static int tcp_chr_new_client(CharDriverState *chr, QIOChannel *ioc)g_source_remove (s->listen_tag); s->listen_tag =0 ; } -tcp_chr_connect (chr); + +if (s->do_telnetopt) { +tcp_chr_telnet_init (chr); + }else { +tcp_chr_connect (chr); + }return 0 ; }
@@ -2863,7 +2917,6 @@ static gboolean tcp_chr_accept(QIOChannel *channel, void *opaque) {CharDriverState *chr = opaque; -TCPCharDriver *s = chr->opaque;QIOChannel *ioc; ioc = QIO_CHANNEL(
@@ -2872,10 +2925,6 @@ static gboolean tcp_chr_accept(QIOChannel *channel,return TRUE; } -if (s->do_telnetopt) { -tcp_chr_telnet_init (ioc); - } -tcp_chr_new_client (chr, ioc);object_unref (OBJECT (ioc));
Signature · 1 line
2.1.0
RFC PATCH 34/34 char: introduce support for TLS encrypted TCP chardev backend
In reply to Daniel P. BerrangeThis integrates support for QIOChannelTLS object in the TCP chardev backend. If the 'tls-cred=NAME' option is passed with the '-chardev tcp' argument, then it will setup the chardev such that the client is required to establish a TLS handshake when connecting. The 'acl' option will further enable the creation of a 'char.$ID.tlspeername' ACL which will be used to validate the client x509 certificate, if provided.
A complete invokation to run QEMU as the server for a TLS encrypted serial dev might be
$ qemu-system-x86_64 \
-nodefconfig -nodefaults -device sga -display none \
-chardev socket,id=s0,host=127.0.0.1,port=9000,tls-cred=tls0,server \
-device isa-serial,chardev=s0 \
-object qcrypto-tls-cred,id=tls0,credtype=x509,\
endpoint=server,dir=/home/berrange/security/qemutls,verify-peer=offTo test with the gnutls-cli tool as the client:
$ gnutls-cli --priority=NORMAL -p 9000 \
--x509cafile=/home/berrange/security/qemutls/ca-cert.pem \
127.0.0.1If QEMU was told to use 'anon' credential type, then use the priority string 'NOMAL:+ANON-DH' with gnutls-cli
Alternatively, if setting up a chardev to operate as a client, then the TLS credentials registered must be for the client endpoint. First a TLS server must be setup, which can be done with the gnutls-serv tool
$ gnutls-serv --priority=NORMAL -p 9000 \
--x509cafile=/home/berrange/security/qemutls/ca-cert.pem \
--x509certfile=/home/berrange/security/qemutls/server-cert.pem \
--x509keyfile=/home/berrange/security/qemutls/server-key.pemThen QEMU can connect with
$ qemu-system-x86_64 \
-nodefconfig -nodefaults -device sga -display none \
-chardev socket,id=s0,host=127.0.0.1,port=9000,tls-cred=tls0 \
-device isa-serial,chardev=s0 \
-object qcrypto-tls-cred,id=tls0,credtype=x509,\
endpoint=client,dir=/home/berrange/security/qemutls--- qapi-schema.json | 2 + qemu-char.c | 182 ++++++++++++++++++++++++++++++++++++++++++++++--------- qemu-options.hx | 9 ++- 3 files changed, 161 insertions(+), 32 deletions(-)
qapi-schema.json +2 -0
@@ -2782,6 +2782,8 @@ # Since: 1.4 ## { 'type': 'ChardevSocket', 'data': { 'addr' : 'SocketAddress', + '*tls-cred' : 'str', + '*acl' : 'str', '*server' : 'bool', '*wait' : 'bool', '*nodelay' : 'bool',
qemu-char.c +152 -30
315 lines not on this page · raw message
qemu-options.hx +7 -2
@@ -2009,7 +2009,7 @@ ETEXI DEF("chardev", HAS_ARG, QEMU_OPTION_chardev, "-chardev null,id=id[,mux=on|off]\n" "-chardev socket,id=id[,host=host],port=port[,to=to][,ipv4][,ipv6][,nodelay][,reconnect=seconds]\n" - " [,server][,nowait][,telnet][,reconnect=seconds][,mux=on|off] (tcp)\n" + " [,server][,nowait][,telnet][,reconnect=seconds][,mux=on|off][,tls-cred=ID][,acl] (tcp)\n" "-chardev socket,id=id,path=path[,server][,nowait][,telnet][,reconnect=seconds][,mux=on|off] (unix)\n" "-chardev udp,id=id[,host=host],port=port[,localaddr=localaddr]\n" " [,localport=localport][,ipv4][,ipv6][,mux=on|off]\n"
@@ -2082,7 +2082,7 @@ Options to each backend are described below. A void device. This device will not emit any data, and will drop any data it receives. The null backend does not take any options. -@item -chardev socket ,id=@var{id} [@var{TCP options} or @var{unix options}] [,server] [,nowait] [,telnet] [,reconnect=@var{seconds}] +@item -chardev socket ,id=@var{id} [@var{TCP options} or @var{unix options}] [,server] [,nowait] [,telnet] [,reconnect=@var{seconds}][,tls-cred=@var{id}] Create a two-way stream socket, which can be either a TCP or a unix socket. A unix socket will be created if @option{path} is specified. Behaviour is
@@ -2100,6 +2100,11 @@ escape sequences. the remote end goes away. qemu will delay this many seconds and then attempt to reconnect. Zero disables reconnecting, and is the default. +@option{tls-cred} requests enablement of the TLS protocol for encryption, +and specifies the id of the TLS credentials to use for the handshake. The +credentials must be previously created with the @option{-object qcrypto-tls-cred} +argument. + TCP and unix socket options are given below: @table @option
Signature · 1 line
2.1.0
Re: [Qemu-devel] [PATCH v1 RFC 34/34] char: introduce support for TLS encrypted TCP chardev backend
Quoted 9 lines · Daniel P. Berrange
This integrates support for QIOChannelTLS object in the TCP chardev backend. If the 'tls-cred=NAME' option is passed with the '-chardev tcp' argument, then it will setup the chardev such that the client is required to establish a TLS handshake when connecting. The 'acl' option will further enable the creation of a 'char.$ID.tlspeername' ACL which will be used to validate the client x509 certificate, if provided.
A complete invokation to run QEMU as the server for a TLS
s/invokation/invocation/
Quoted 9 lines
encrypted serial dev might be
$ qemu-system-x86_64 \ -nodefconfig -nodefaults -device sga -display none \ -chardev socket,id=s0,host=127.0.0.1,port=9000,tls-cred=tls0,server \ -device isa-serial,chardev=s0 \ -object qcrypto-tls-cred,id=tls0,credtype=x509,\ endpoint=server,dir=/home/berrange/security/qemutls,verify-peer=off
Quoted 8 lines
To test with the gnutls-cli tool as the client:
$ gnutls-cli --priority=NORMAL -p 9000 \ --x509cafile=/home/berrange/security/qemutls/ca-cert.pem \ 127.0.0.1If QEMU was told to use 'anon' credential type, then use the priority string 'NOMAL:+ANON-DH' with gnutls-cli
s/NOMAL/NORMAL/
Quoted 32 lines
Alternatively, if setting up a chardev to operate as a client, then the TLS credentials registered must be for the client endpoint. First a TLS server must be setup, which can be done with the gnutls-serv tool
$ gnutls-serv --priority=NORMAL -p 9000 \ --x509cafile=/home/berrange/security/qemutls/ca-cert.pem \ --x509certfile=/home/berrange/security/qemutls/server-cert.pem \ --x509keyfile=/home/berrange/security/qemutls/server-key.pemThen QEMU can connect with
$ qemu-system-x86_64 \ -nodefconfig -nodefaults -device sga -display none \ -chardev socket,id=s0,host=127.0.0.1,port=9000,tls-cred=tls0 \ -device isa-serial,chardev=s0 \ -object qcrypto-tls-cred,id=tls0,credtype=x509,\ endpoint=client,dir=/home/berrange/security/qemutlsSigned-off-by Daniel P. Berrange <[email protected]>--- qapi-schema.json | 2 + qemu-char.c | 182 ++++++++++++++++++++++++++++++++++++++++++++++--------- qemu-options.hx | 9 ++- 3 files changed, 161 insertions(+), 32 deletions(-)qapi-schema.json +0
-0
qapi-schema.json line 2786
Quoted 5 lines
qapi-schema.json +2
-0@@ -2782,6 +2782,8 @@ # Since: 1.4 ## { 'type': 'ChardevSocket', 'data': { 'addr' : 'SocketAddress', + '*tls-cred' : 'str', + '*acl' : 'str',
Need to document these two fields, along with '(since 2.4)' designators.
Quoted 11 lines
qapi-schema.json +2
-1+++ b/qemu-options.hx@@ -2009,7 +2009,7 @@ ETEXI DEF("chardev", HAS_ARG, QEMU_OPTION_chardev, "-chardev null,id=id[,mux=on|off]\n" "-chardev socket,id=id[,host=host],port=port[,to=to][,ipv4][,ipv6][,nodelay][,reconnect=seconds]\n" - " [,server][,nowait][,telnet][,reconnect=seconds][,mux=on|off] (tcp)\n" + " [,server][,nowait][,telnet][,reconnect=seconds][,mux=on|off][,tls-cred=ID][,acl] (tcp)\n" "-chardev socket,id=id,path=path[,server][,nowait][,telnet][,reconnect=seconds][,mux=on|off] (unix)\n" "-chardev udp,id=id[,host=host],port=port[,localaddr=localaddr]\n" " [,localport=localport][,ipv4][,ipv6][,mux=on|off]\n"
qapi-schema.json Options to each backend are described below. · line 2085
Quoted 5 lines
qapi-schema.json +1
-1@@ -2082,7 +2082,7 @@ Options to each backend are described below. A void device. This device will not emit any data, and will drop any data it receives. The null backend does not take any options. -@item -chardev socket ,id=@var{id} [@var{TCP options} or @var{unix options}] [,server] [,nowait] [,telnet] [,reconnect=@var{seconds}] +@item -chardev socket ,id=@var{id} [@var{TCP options} or @var{unix options}] [,server] [,nowait] [,telnet] [,reconnect=@var{seconds}][,tls-cred=@var{id}]
Everyone else in this line had space before [
Signature · 2 lines
Eric Blake eblake redhat com +1-919-301-3266 Libvirt virtualization library http://libvirt.org
Re: [Qemu-devel] [PATCH v1 RFC 34/34] char: introduce support for TLS encrypted TCP chardev backend
Quoted 60 lines · Eric Blake
Quoted 9 lines · Daniel P. Berrange
This integrates support for QIOChannelTLS object in the TCP chardev backend. If the 'tls-cred=NAME' option is passed with the '-chardev tcp' argument, then it will setup the chardev such that the client is required to establish a TLS handshake when connecting. The 'acl' option will further enable the creation of a 'char.$ID.tlspeername' ACL which will be used to validate the client x509 certificate, if provided.
A complete invokation to run QEMU as the server for a TLS
s/invokation/invocation/
Quoted 9 lines
encrypted serial dev might be
$ qemu-system-x86_64 \ -nodefconfig -nodefaults -device sga -display none \ -chardev socket,id=s0,host=127.0.0.1,port=9000,tls-cred=tls0,server \ -device isa-serial,chardev=s0 \ -object qcrypto-tls-cred,id=tls0,credtype=x509,\ endpoint=server,dir=/home/berrange/security/qemutls,verify-peer=offQuoted 8 lines
To test with the gnutls-cli tool as the client:
$ gnutls-cli --priority=NORMAL -p 9000 \ --x509cafile=/home/berrange/security/qemutls/ca-cert.pem \ 127.0.0.1If QEMU was told to use 'anon' credential type, then use the priority string 'NOMAL:+ANON-DH' with gnutls-cli
s/NOMAL/NORMAL/
Quoted 27 lines
Alternatively, if setting up a chardev to operate as a client, then the TLS credentials registered must be for the client endpoint. First a TLS server must be setup, which can be done with the gnutls-serv tool
$ gnutls-serv --priority=NORMAL -p 9000 \ --x509cafile=/home/berrange/security/qemutls/ca-cert.pem \ --x509certfile=/home/berrange/security/qemutls/server-cert.pem \ --x509keyfile=/home/berrange/security/qemutls/server-key.pemThen QEMU can connect with
$ qemu-system-x86_64 \ -nodefconfig -nodefaults -device sga -display none \ -chardev socket,id=s0,host=127.0.0.1,port=9000,tls-cred=tls0 \ -device isa-serial,chardev=s0 \ -object qcrypto-tls-cred,id=tls0,credtype=x509,\ endpoint=client,dir=/home/berrange/security/qemutlsSigned-off-by Daniel P. Berrange <[email protected]>--- qapi-schema.json | 2 + qemu-char.c | 182 ++++++++++++++++++++++++++++++++++++++++++++++--------- qemu-options.hx | 9 ++- 3 files changed, 161 insertions(+), 32 deletions(-)
Quoted 12 lines
qapi-schema.json line 2786
Quoted 10 lines
qapi-schema.json +2
-0@@ -2782,6 +2782,8 @@ # Since: 1.4 ## { 'type': 'ChardevSocket', 'data': { 'addr' : 'SocketAddress', + '*tls-cred' : 'str', + '*acl' : 'str',Need to document these two fields, along with '(since 2.4)' designators.
Ah, ok forgot about that.
Regards,
Daniel
Signature · 4 lines
|: http://berrange.com -o- http://www.flickr.com/photos/dberrange/ :| |: http://libvirt.org -o- http://virt-manager.org :| |: http://autobuild.org -o- http://search.cpan.org/~danberr/ :| |: http://entangle-photo.org -o- http://live.gnome.org/gtk-vnc :|
Re: [Qemu-devel] [PATCH v1 RFC 34/34] char: introduce support for TLS encrypted TCP chardev backend
In reply to Daniel P. BerrangeQuoted 37 lines · Daniel P. Berrange
This integrates support for QIOChannelTLS object in the TCP chardev backend. If the 'tls-cred=NAME' option is passed with the '-chardev tcp' argument, then it will setup the chardev such that the client is required to establish a TLS handshake when connecting. The 'acl' option will further enable the creation of a 'char.$ID.tlspeername' ACL which will be used to validate the client x509 certificate, if provided.
A complete invokation to run QEMU as the server for a TLS encrypted serial dev might be
$ qemu-system-x86_64 \ -nodefconfig -nodefaults -device sga -display none \ -chardev socket,id=s0,host=127.0.0.1,port=9000,tls-cred=tls0,server \ -device isa-serial,chardev=s0 \ -object qcrypto-tls-cred,id=tls0,credtype=x509,\ endpoint=server,dir=/home/berrange/security/qemutls,verify-peer=offTo test with the gnutls-cli tool as the client:
$ gnutls-cli --priority=NORMAL -p 9000 \ --x509cafile=/home/berrange/security/qemutls/ca-cert.pem \ 127.0.0.1If QEMU was told to use 'anon' credential type, then use the priority string 'NOMAL:+ANON-DH' with gnutls-cli
Alternatively, if setting up a chardev to operate as a client, then the TLS credentials registered must be for the client endpoint. First a TLS server must be setup, which can be done with the gnutls-serv tool
$ gnutls-serv --priority=NORMAL -p 9000 \ --x509cafile=/home/berrange/security/qemutls/ca-cert.pem \ --x509certfile=/home/berrange/security/qemutls/server-cert.pem \ --x509keyfile=/home/berrange/security/qemutls/server-key.pem
Quoted 8 lines
Then QEMU can connect with
$ qemu-system-x86_64 \ -nodefconfig -nodefaults -device sga -display none \ -chardev socket,id=s0,host=127.0.0.1,port=9000,tls-cred=tls0 \ -device isa-serial,chardev=s0 \ -object qcrypto-tls-cred,id=tls0,credtype=x509,\ endpoint=client,dir=/home/berrange/security/qemutls
I've applied your 'qemu-io-channel-7' branch locally, compiled QEMU and began to play around.
$ git describe
v2.3.0-rc3-42-g5878696When running QEMU either as server or as client, I notice this error (further below are the details of how I tested):
[. . .]
qemu-system-x86_64: -object qcrypto-tls-cred,id=tls0,credtype=x509,:
invalid object type: qcrypto-tls-credTest with QEMU as client
------------------------
Setup PKI environment[1] , and run a GnuTLS server:
$ gnutls-serv --priority=NORMAL -p 9000 \
--x509cafile=/export/security/gnutls/ca-cert.pem \
--x509certfile=/export/security/gnutls/server-cert.pem \
--x509keyfile=/export/security/gnutls/server-key.pem
Set static Diffie-Hellman parameters, consider --dhparams.
Processed 1 CA certificate(s).
HTTP Server listening on IPv4 0.0.0.0 port 9000...done
HTTP Server listening on IPv6 :: port 9000...doneAnd, connect with QEMU:
$ /home/kashyapc/build/tls-qemu/x86_64-softmmu/qemu-system-x86_64 \
-nodefconfig -nodefaults -device sga -display none \
-chardev socket,id=s0,host=localhost,port=9000,tls-cred=tls0 \
-device isa-serial,chardev=s0 \
-object qcrypto-tls-cred,id=tls0,credtype=x509,\
endpoint=client,dir=/export/security/gnutls
qemu-system-x86_64: -object qcrypto-tls-cred,id=tls0,credtype=x509,:
invalid object type: qcrypto-tls-credTest with QEMU as server
------------------------
$ /home/kashyapc/build/tls-qemu/x86_64-softmmu/qemu-system-x86_64 \
-nodefconfig -nodefaults -device sga -display none \
-chardev socket,id=s0,host=localhost,port=9000,tls-cred=tls0,server \
-device isa-serial,chardev=s0 \
-object qcrypto-tls-cred,id=tls0,credtype=x509,\
endpoint=server,dir=/export/security/gnutls,verify-peer=off
qemu-system-x86_64: -object qcrypto-tls-cred,id=tls0,credtype=x509,:
invalid object type: qcrypto-tls-credAm I missing something simple?
Additional notes
----------------
(a) I verified the QEMU CLI for -object is correct by looking at local the
'qemu-options.hx' file:
@item -object
qcrypto-tls-cred,id=@var{id},credtype=@var{type},endpoint=@var{endpoint},
dir=@var{/path/to/cred/dir},verify-peer=@var{on|off}(b) Just to ensure that TLS server is setup correctly, I validated it via gnutls-cli:
$ gnutls-cli --priority=NORMAL -p 9000 \
--x509cafile=/export/security/gnutls/ca-cert.pem localhost
[. . .]
- Status: The certificate is trusted.
- Successfully sent 0 certificate(s) to server.
- Compression: NULL
- Options: safe renegotiation,
- Handshake was completed
[. . .]
(c) Exact CLI invocatoins of how I created the self-signed CA, server certificate including their outputs are noted here[1].
(d) When creating the server certificate request, I used the 'dnsName' attribute, and gave its value as "localhost".
[1] https://kashyapc.fedorapeople.org/gnutls-pki-setup.txt
Signature · 1 line
/kashyap
Re: [Qemu-devel] [PATCH v1 RFC 34/34] char: introduce support for TLS encrypted TCP chardev backend
Quoted 53 lines · Kashyap Chamarthy
Quoted 37 lines · Daniel P. Berrange
This integrates support for QIOChannelTLS object in the TCP chardev backend. If the 'tls-cred=NAME' option is passed with the '-chardev tcp' argument, then it will setup the chardev such that the client is required to establish a TLS handshake when connecting. The 'acl' option will further enable the creation of a 'char.$ID.tlspeername' ACL which will be used to validate the client x509 certificate, if provided.
A complete invokation to run QEMU as the server for a TLS encrypted serial dev might be
$ qemu-system-x86_64 \ -nodefconfig -nodefaults -device sga -display none \ -chardev socket,id=s0,host=127.0.0.1,port=9000,tls-cred=tls0,server \ -device isa-serial,chardev=s0 \ -object qcrypto-tls-cred,id=tls0,credtype=x509,\ endpoint=server,dir=/home/berrange/security/qemutls,verify-peer=offTo test with the gnutls-cli tool as the client:
$ gnutls-cli --priority=NORMAL -p 9000 \ --x509cafile=/home/berrange/security/qemutls/ca-cert.pem \ 127.0.0.1If QEMU was told to use 'anon' credential type, then use the priority string 'NOMAL:+ANON-DH' with gnutls-cli
Alternatively, if setting up a chardev to operate as a client, then the TLS credentials registered must be for the client endpoint. First a TLS server must be setup, which can be done with the gnutls-serv tool
$ gnutls-serv --priority=NORMAL -p 9000 \ --x509cafile=/home/berrange/security/qemutls/ca-cert.pem \ --x509certfile=/home/berrange/security/qemutls/server-cert.pem \ --x509keyfile=/home/berrange/security/qemutls/server-key.pemQuoted 8 lines
Then QEMU can connect with
$ qemu-system-x86_64 \ -nodefconfig -nodefaults -device sga -display none \ -chardev socket,id=s0,host=127.0.0.1,port=9000,tls-cred=tls0 \ -device isa-serial,chardev=s0 \ -object qcrypto-tls-cred,id=tls0,credtype=x509,\ endpoint=client,dir=/home/berrange/security/qemutlsI've applied your 'qemu-io-channel-7' branch locally, compiled QEMU and began to play around.
$ git describe v2.3.0-rc3-42-g5878696
Quoted 6 lines
When running QEMU either as server or as client, I notice this error (further below are the details of how I tested):
[. . .] qemu-system-x86_64: -object qcrypto-tls-cred,id=tls0,credtype=x509,: invalid object type: qcrypto-tls-cred
Typo in my commit message - it should end in '-creds' not '-cred' for the object type.
Regards,
Daniel
Signature · 4 lines
|: http://berrange.com -o- http://www.flickr.com/photos/dberrange/ :| |: http://libvirt.org -o- http://virt-manager.org :| |: http://autobuild.org -o- http://search.cpan.org/~danberr/ :| |: http://entangle-photo.org -o- http://live.gnome.org/gtk-vnc :|
Re: [Qemu-devel] [PATCH v1 RFC 34/34] char: introduce support for TLS encrypted TCP chardev backend
Quoted 4 lines · Daniel P. Berrange
Quoted 2 lines
Quoted 2 lines
qemu-system-x86_64: -object qcrypto-tls-cred,id=tls0,credtype=x509,:
invalid object type: qcrypto-tls-credTypo in my commit message - it should end in '-creds' not '-cred' for the object type.
FWIW, I think just "tls-creds" is probably a better name for the -object option. "qcrypto" is more of an internal detail.
Paolo
Re: [Qemu-devel] [PATCH v1 RFC 34/34] char: introduce support for TLS encrypted TCP chardev backend
Quoted 10 lines · Paolo Bonzini
Quoted 4 lines · Daniel P. Berrange
Quoted 2 lines
Quoted 2 lines
qemu-system-x86_64: -object qcrypto-tls-cred,id=tls0,credtype=x509,:
invalid object type: qcrypto-tls-credTypo in my commit message - it should end in '-creds' not '-cred' for the object type.
FWIW, I think just "tls-creds" is probably a better name for the -object option. "qcrypto" is more of an internal detail.
Ok, I'll drop the prefix from the name - I guess chance of a namespace clash is pretty low anyway.
Regards,
Daniel
Signature · 4 lines
|: http://berrange.com -o- http://www.flickr.com/photos/dberrange/ :| |: http://libvirt.org -o- http://virt-manager.org :| |: http://autobuild.org -o- http://search.cpan.org/~danberr/ :| |: http://entangle-photo.org -o- http://live.gnome.org/gtk-vnc :|
Re: [Qemu-devel] [PATCH v1 RFC 34/34] char: introduce support for TLS encrypted TCP chardev backend
In reply to Daniel P. BerrangeQuoted 57 lines · Daniel P. Berrange
Quoted 56 lines · Kashyap Chamarthy
Quoted 37 lines · Daniel P. Berrange
This integrates support for QIOChannelTLS object in the TCP chardev backend. If the 'tls-cred=NAME' option is passed with the '-chardev tcp' argument, then it will setup the chardev such that the client is required to establish a TLS handshake when connecting. The 'acl' option will further enable the creation of a 'char.$ID.tlspeername' ACL which will be used to validate the client x509 certificate, if provided.
A complete invokation to run QEMU as the server for a TLS encrypted serial dev might be
$ qemu-system-x86_64 \ -nodefconfig -nodefaults -device sga -display none \ -chardev socket,id=s0,host=127.0.0.1,port=9000,tls-cred=tls0,server \ -device isa-serial,chardev=s0 \ -object qcrypto-tls-cred,id=tls0,credtype=x509,\ endpoint=server,dir=/home/berrange/security/qemutls,verify-peer=offTo test with the gnutls-cli tool as the client:
$ gnutls-cli --priority=NORMAL -p 9000 \ --x509cafile=/home/berrange/security/qemutls/ca-cert.pem \ 127.0.0.1If QEMU was told to use 'anon' credential type, then use the priority string 'NOMAL:+ANON-DH' with gnutls-cli
Alternatively, if setting up a chardev to operate as a client, then the TLS credentials registered must be for the client endpoint. First a TLS server must be setup, which can be done with the gnutls-serv tool
$ gnutls-serv --priority=NORMAL -p 9000 \ --x509cafile=/home/berrange/security/qemutls/ca-cert.pem \ --x509certfile=/home/berrange/security/qemutls/server-cert.pem \ --x509keyfile=/home/berrange/security/qemutls/server-key.pemQuoted 8 lines
Then QEMU can connect with
$ qemu-system-x86_64 \ -nodefconfig -nodefaults -device sga -display none \ -chardev socket,id=s0,host=127.0.0.1,port=9000,tls-cred=tls0 \ -device isa-serial,chardev=s0 \ -object qcrypto-tls-cred,id=tls0,credtype=x509,\ endpoint=client,dir=/home/berrange/security/qemutlsI've applied your 'qemu-io-channel-7' branch locally, compiled QEMU and began to play around.
$ git describe v2.3.0-rc3-42-g5878696When running QEMU either as server or as client, I notice this error (further below are the details of how I tested):
Quoted 6 lines
Quoted 3 lines
[. . .] qemu-system-x86_64: -object qcrypto-tls-cred,id=tls0,credtype=x509,: invalid object type: qcrypto-tls-credTypo in my commit message - it should end in '-creds' not '-cred' for the object type.
Yep, that fixed it. I should have looked deeper -- your example in include/crypto/tlscreds.h has the correct syntax and also includes a QMP variant. Just to note, there seems to be three instances of this typo in qemu-options.hx (found via git grep qcrypto-tls-cred).
While running QEMU as TLS server, the TLS handshake completes successfully when connected via gnutls-cli.
However, when using QEMU as client to connect to an existing GnuTLS server, I notice a segmentation fault:
$ /home/kashyapc/build/tls-qemu/x86_64-softmmu/qemu-system-x86_64 \
-nodefconfig -nodefaults -device sga -display none \
-chardev socket,id=s0,host=localhost,port=9000,tls-cred=tls0 \
-device isa-serial,chardev=s0 \
-object qcrypto-tls-creds,id=tls0,credtype=x509,endpoint=client,dir=/export/security/gnutls
Segmentation fault (core dumped)Signature · 1 line
/kashyap
Re: [Qemu-devel] [PATCH v1 RFC 34/34] char: introduce support for TLS encrypted TCP chardev backend
On Tue, May 05, 2015 at 04:54:44PM +0200, Kashyap Chamarthy wrote:
[. . .]
Quoted 6 lines
While running QEMU as TLS server, the TLS handshake completes successfully when connected via
gnutls-cli.However, when using QEMU as client to connect to an existing GnuTLS server, I notice a segmentation fault:
Quoted 6 lines
$ /home/kashyapc/build/tls-qemu/x86_64-softmmu/qemu-system-x86_64 \
-nodefconfig -nodefaults -device sga -display none \
-chardev socket,id=s0,host=localhost,port=9000,tls-cred=tls0 \
-device isa-serial,chardev=s0 \
-object qcrypto-tls-creds,id=tls0,credtype=x509,endpoint=client,dir=/export/security/gnutls
Segmentation fault (core dumped)Some debugging with gdb below.
QEMU was built with:
./configure --target-list=x86_64-softmmu --enable-debug
make -j4Stack traces:
$ gdb /home/kashyapc/build/tls-qemu/x86_64-softmmu/qemu-system-x86_64 [. . .] (gdb) run -nodefconfig -nodefaults -device sga -display none -chardev socket,id=s0,host=localhost,port=9000,tls-cred=tls0 -device isa-serial,chardev=s0 -object qcrypto-tls-creds,id=tls0,credtype=x509,endpoint=client,dir=/export/security/gnutls Starting program: /home/kashyapc/build/tls-qemu/x86_64-softmmu/qemu-system-x86_64 -nodefconfig -nodefaults -device sga -display none -chardev socket,id=s0,host=localhost,port=9000,tls-cred=tls0 -device isa-serial,chardev=s0 -object qcrypto-tls-creds,id=tls0,credtype=x509,endpoint=client,dir=/export/security/gnutls [. . .] Program received signal SIGSEGV, Segmentation fault. __strstr_sse2_unaligned () at ../sysdeps/x86_64/multiarch/strstr-sse2-unaligned.S:40 40 movdqu (%rdi), %xmm3 (gdb) thread apply all bt full
Thread 2 (Thread 0x7fffe4fcc700 (LWP 5393)):
#0 0x00007ffff6bce8fd in nanosleep () at ../sysdeps/unix/syscall-template.S:81
#1 0x00007ffff64f1de8 in g_usleep () at /lib64/libglib-2.0.so.0
#2 0x00005555559d32d7 in call_rcu_thread (opaque=0x0) at /home/kashyapc/tinker-space/qemu/util/rcu.c:228
tries = 0
n = 0
node = 0x7ffff7fd19a0#3 0x00007ffff6bc652a in start_thread (arg=0x7fffe4fcc700) at pthread_create.c:310
__res = <optimized out>
pd = 0x7fffe4fcc700
now = <optimized out>
unwind_buf =
{cancel_jmp_buf = {{jmp_buf = {140737035159296, 3180389637749088242, 140737488345857, 4096, 140737035159296, 140737035160000, -3180444589616128014, -3180404459381186574}, mask_was_saved = 0}}, priv = {pad = {0x0, 0x0, 0x0, 0x0}, data = {prev = 0x0, cleanup = 0x0, canceltype = 0}}}
not_first_call = <optimized out>
pagesize_m1 = <optimized out>
sp = <optimized out>
freesize = <optimized out>
#4 0x00007fffeea0979d in clone () at ../sysdeps/unix/sysv/linux/x86_64/clone.S:109
---Type <return> to continue, or q <return> to quit--- Thread 1 (Thread 0x7ffff7f89bc0 (LWP 5389)):
#0 0x00007fffee9ae6dd in __strstr_sse2_unaligned () at ../sysdeps/x86_64/multiarch/strstr-sse2-unaligned.S:40
#1 0x00007ffff1c6b370 in _gnutls_url_is_known () at /lib64/libgnutls.so.28
#2 0x00007ffff1c6b3d9 in gnutls_certificate_set_x509_key_file2 () at /lib64/libgnutls.so.28
#3 0x00005555559aba85 in qcrypto_tls_creds_load_x509 (creds=0x55555639ac60, errp=0x7fffffffd8d8) at /home/kashyapc/tinker-space/qemu/crypto/tlscreds.c:728
cacert = 0x55555639a8c0 "/export/security/gnutls/ca-cert.pem"
cacrl = 0x0
cert = 0x0
key = 0x0
dhparams = 0x0
ret = 1
rv = -1
#4 0x00005555559abdb2 in qcrypto_tls_creds_load (creds=0x55555639ac60, errp=0x7fffffffd8d8) at /home/kashyapc/tinker-space/qemu/crypto/tlscreds.c:820
#5 0x00005555559abf30 in qcrypto_tls_creds_prop_set_loaded (obj=0x55555639ac60, value=true, errp=0x7fffffffd8d8) at /home/kashyapc/tinker-space/qemu/crypto/tlscreds.c:888
creds = 0x55555639ac60
__func__ = "qcrypto_tls_creds_prop_set_loaded"
#6 0x00005555558cec1c in property_set_bool (obj=0x55555639ac60, v=0x55555639b4d0, opaque=0x55555639ad40, name=0x555555a59695 "loaded", errp=0x7fffffffd8d8)
at /home/kashyapc/tinker-space/qemu/qom/object.c:1600
prop = 0x55555639ad40
value = true
local_err = 0x0
---Type <return> to continue, or q <return> to quit---
#7 0x00005555558cd485 in object_property_set (obj=0x55555639ac60, v=0x55555639b4d0, name=0x555555a59695 "loaded", errp=0x7fffffffd8d8) at /home/kashyapc/tinker-space/qemu/qom/object.c:901
prop = 0x55555639ad60
#8 0x00005555558cfa47 in object_property_set_qobject (obj=0x55555639ac60, value=0x55555639b200, name=0x555555a59695 "loaded", errp=0x7fffffffd8d8)
at /home/kashyapc/tinker-space/qemu/qom/qom-qobject.c:24
mi = 0x55555639b4d0
#9 0x00005555558cd6f4 in object_property_set_bool (obj=0x55555639ac60, value=true, name=0x555555a59695 "loaded", errp=0x7fffffffd8d8) at /home/kashyapc/tinker-space/qemu/qom/object.c:969
qbool = 0x55555639b200
#10 0x00005555559ac2e5 in qcrypto_tls_creds_complete (uc=0x55555639ac60, errp=0x7fffffffd8d8) at /home/kashyapc/tinker-space/qemu/crypto/tlscreds.c:1018
#11 0x00005555558d0899 in user_creatable_complete (obj=0x55555639ac60, errp=0x7fffffffd8d8) at /home/kashyapc/tinker-space/qemu/qom/object_interfaces.c:17
ucc = 0x5555563702f0
uc = 0x55555639ac60
__func__ = "user_creatable_complete"
#12 0x0000555555750201 in object_add (type=0x55555639a8f0 "qcrypto-tls-creds", id=0x55555639a850 "tls0", qdict=0x5555563997b0, v=0x5555563996a0, errp=0x7fffffffd920)
at /home/kashyapc/tinker-space/qemu/qmp.c:659
obj = 0x55555639ac60
klass = 0x555556370050
e = 0x0
local_err = 0x0
#13 0x0000555555736a2d in object_create (opts=0x55555638a7e0, opaque=0x55555573684e <object_create_phase1>) at /home/kashyapc/tinker-space/qemu/vl.c:2644
err = 0x0
type = 0x55555639a8f0 "qcrypto-tls-creds"
---Type <return> to continue, or q <return> to quit---
id = 0x55555639a850 "tls0"
dummy = 0x55555639aaf0
ov = 0x5555563996a0
pdict = 0x5555563997b0
type_predicate = 0x55555573684e <object_create_phase1>
#14 0x00005555559d08e0 in qemu_opts_foreach (list=0x555555e12ee0 <qemu_object_opts>, func=0x5555557368aa <object_create>, opaque=0x55555573684e <object_create_phase1>, abort_on_failure=0)
at /home/kashyapc/tinker-space/qemu/util/qemu-option.c:1059
loc = {kind = LOC_CMDLINE, num = 2, ptr = 0x7fffffffde10, prev = 0x555556315300 <std_loc>}
opts = 0x55555638a7e0
rc = 0
#15 0x000055555573a273 in main (argc=13, argv=0x7fffffffddb8, envp=0x7fffffffde28) at /home/kashyapc/tinker-space/qemu/vl.c:4039
i = 21845
snapshot = 0
linux_boot = 3
initrd_filename = 0xffff800000002441 <error: Cannot access memory at address 0xffff800000002441>
kernel_filename = 0xffffffffffffffff <error: Cannot access memory at address 0xffffffffffffffff>
kernel_cmdline = 0x555556345060 "\241x\244UUU"
boot_order = 0x0
boot_once = 0x0
ds = 0x7fffffffdbbf
cyls = 0
---Type <return> to continue, or q <return> to quit---
heads = 0
secs = 0
translation = 0
hda_opts = 0x0
opts = 0x55555638aa50
machine_opts = 0xffffffffffffffff
icount_opts = 0x0
olist = 0x0
optind = 13
optarg = 0x0
loadvm = 0x0
machine_class = 0x55555637ac70
cpu_model = 0x0
vga_model = 0x0
qtest_chrdev = 0x0
qtest_log = 0x0
pid_file = 0x0
incoming = 0x0
show_vnc_port = 0
defconfig = false
userconfig = true
---Type <return> to continue, or q <return> to quit---
log_mask = 0x0
log_file = 0x0
mem_trace =
{malloc = 0x5555557366c1 <malloc_and_trace>, realloc = 0x5555557366f6 <realloc_and_trace>, free = 0x55555573673a <free_and_trace>, calloc = 0x0, try_malloc = 0x0, try_realloc = 0x0}
trace_events = 0x0
trace_file = 0x0
maxram_size = 134217728
ram_slots = 0
vmstate_dump_file = 0x0
main_loop_err = 0x0
err = 0x0
__func__ = "main"Signature · 1 line
/kashyap
Re: [Qemu-devel] [PATCH v1 RFC 34/34] char: introduce support for TLS encrypted TCP chardev backend
Quoted 30 lines · Kashyap Chamarthy
On Tue, May 05, 2015 at 04:54:44PM +0200, Kashyap Chamarthy wrote:
[. . .]
Quoted 6 lines
While running QEMU as TLS server, the TLS handshake completes successfully when connected via
gnutls-cli.However, when using QEMU as client to connect to an existing GnuTLS server, I notice a segmentation fault:
Quoted 6 lines
$ /home/kashyapc/build/tls-qemu/x86_64-softmmu/qemu-system-x86_64 \ -nodefconfig -nodefaults -device sga -display none \ -chardev socket,id=s0,host=localhost,port=9000,tls-cred=tls0 \ -device isa-serial,chardev=s0 \ -object qcrypto-tls-creds,id=tls0,credtype=x509,endpoint=client,dir=/export/security/gnutls Segmentation fault (core dumped)Some debugging with
gdbbelow.QEMU was built with:
./configure --target-list=x86_64-softmmu --enable-debug make -j4Stack traces:
$ gdb /home/kashyapc/build/tls-qemu/x86_64-softmmu/qemu-system-x86_64#2
0x00007ffff1c6b3d9ingnutls_certificate_set_x509_key_file2() at/lib64/libgnutls.so.28
#30x00005555559aba85inqcrypto_tls_creds_load_x509(creds=0x55555639ac60, errp=0x7fffffffd8d8) at/home/kashyapc/tinker-space/qemu/crypto/tlscreds.c:728
Quoted 7 lines
cacert = 0x55555639a8c0 "/export/security/gnutls/ca-cert.pem"
cacrl = 0x0
cert = 0x0
key = 0x0
dhparams = 0x0
ret = 1
rv = -1Ah, with QEMU running in client mode, the client cert + key are optional. In this case you've not provided them (cert & key are 0x0 ie NULL). We are then mistakenly calling gnutls_certificate_set_x509_key_file2 - if I simply skip that I'll avoid the crash. Thanks for testing this - I'll add a test case to validate this scenario too
Regards,
Daniel
Signature · 4 lines
|: http://berrange.com -o- http://www.flickr.com/photos/dberrange/ :| |: http://libvirt.org -o- http://virt-manager.org :| |: http://autobuild.org -o- http://search.cpan.org/~danberr/ :| |: http://entangle-photo.org -o- http://live.gnome.org/gtk-vnc :|
Re: [Qemu-devel] [PATCH v1 RFC 34/34] char: introduce support for TLS encrypted TCP chardev backend
Quoted 39 lines · Daniel P. Berrange
Quoted 30 lines · Kashyap Chamarthy
On Tue, May 05, 2015 at 04:54:44PM +0200, Kashyap Chamarthy wrote:
[. . .]
Quoted 6 lines
While running QEMU as TLS server, the TLS handshake completes successfully when connected via
gnutls-cli.However, when using QEMU as client to connect to an existing GnuTLS server, I notice a segmentation fault:
Quoted 6 lines
$ /home/kashyapc/build/tls-qemu/x86_64-softmmu/qemu-system-x86_64 \ -nodefconfig -nodefaults -device sga -display none \ -chardev socket,id=s0,host=localhost,port=9000,tls-cred=tls0 \ -device isa-serial,chardev=s0 \ -object qcrypto-tls-creds,id=tls0,credtype=x509,endpoint=client,dir=/export/security/gnutls Segmentation fault (core dumped)Some debugging with
gdbbelow.QEMU was built with:
./configure --target-list=x86_64-softmmu --enable-debug make -j4Stack traces:
$ gdb /home/kashyapc/build/tls-qemu/x86_64-softmmu/qemu-system-x86_64#2
0x00007ffff1c6b3d9ingnutls_certificate_set_x509_key_file2() at/lib64/libgnutls.so.28
#30x00005555559aba85inqcrypto_tls_creds_load_x509(creds=0x55555639ac60, errp=0x7fffffffd8d8) at/home/kashyapc/tinker-space/qemu/crypto/tlscreds.c:728Quoted 7 lines
cacert = 0x55555639a8c0 "/export/security/gnutls/ca-cert.pem" cacrl = 0x0 cert = 0x0 key = 0x0 dhparams = 0x0 ret = 1 rv = -1
Quoted 3 lines
Ah, with QEMU running in client mode, the client cert + key are optional. In this case you've not provided them (cert & key are
0x0ie NULL).
Yep, I generated a client-key.pem, client-cert.pem and placed them in the same gnutls directory where the ca* and server* files are located. Indeed the TLS handshake completes succesfully (tested with the same QEMU CLI as above placed in a script):
$ ./chardev-backend-qemu-as-tls-client.sh Handshake still running 2 Handshake still running 2 Handshake compelte session=0x7f8bdda6f4c0
On the GnuTLS server:
[. . .]
* Accepted connection from IPv4 127.0.0.1 port 51353 on Wed May 6 13:19:10 2015
- Description: (TLS1.2)-(ECDHE-RSA-SECP256R1)-(AES-128-CBC)-(SHA1) [. . .]
- Server has requested a certificate.
- Certificate type: X.509
- Got a certificate list of 1 certificates.
[. . .]
- Version: TLS1.2
- Key Exchange: ECDHE-RSA
- Server Signature: RSA-SHA256
- Client Signature: RSA-SHA256
- Compression: NULL
- Options: safe renegotiation,
- Channel binding 'tls-unique': 7f4ae1e0dc02dbad602a9c27
Quoted 3 lines
We are then mistakenly calling
gnutls_certificate_set_x509_key_file2- if I simply skip that I'll avoid the crash. Thanks for testing this - I'll add a test case to validate this scenario too
Signature · 1 line
/kashyap
Quoted 11 lines · Daniel P. Berrange
While I will probably start work on it, I'm not intending to submit the update of the NBD/migration code, until this series has been positively reviewed and looks like it is close to accepted for merge, as there are already enough patches in this series as it is :-) The aim though is to convert the NBD code to use QIOChannel instead of direct sockets usage & add the TLS protocol extension previously discussed with the NBD spec maintainer. The migration code will either be adapted to use QIOChannel, or the QEMUFile code will be adapted to use QIOChannel. Undecided which is the best approach there at this time. Probably depends whether we can do a QIOChannelRDMA impl that has performance on parity with what exists today.
Thanks for working on this. I have only skimmed the patches but I think your approach makes sense.
Looking forward to digging into NBD patches later.
Stefan