All of lore.kernel.org
 help / color / mirror / Atom feed
* [Qemu-devel] [PATCH 00/15] Implement TLS support to QEMU NBD server & client
@ 2015-11-27 12:20 Daniel P. Berrange
  2015-11-27 12:20 ` [Qemu-devel] [PATCH 01/15] qom: add user_creatable_add & user_creatable_del methods Daniel P. Berrange
                   ` (16 more replies)
  0 siblings, 17 replies; 23+ messages in thread
From: Daniel P. Berrange @ 2015-11-27 12:20 UTC (permalink / raw)
  To: qemu-devel; +Cc: Paolo Bonzini, qemu-block, Wouter Verhelst

This series of patches implements support for TLS in the QEMU NBD
server and client code.

It is implementing the NBD_OPT_STARTTLS option that was previously
discussed here:

  https://www.redhat.com/archives/libvir-list/2014-October/msg00506.html

And is also described in the NBD spec here:

  https://github.com/yoe/nbd/blob/master/doc/proto.md

Based on my impl I think the NBD_OPT_STARTTLS specification is
fine. In my impl, when TLS is requested in the server, I chose
to *refuse* all NBD options until NBD_OPT_STARTTLS has completed
successfully. There is an annoying thing about the fixed new style
protocol in that it made an exception for NBD_OPT_EXPORT_NAME,
so that it is still not possible to report errors for that option.

[qupte]
 - The server will reply to any option apart from `NBD_OPT_EXPORT_NAME`
    with reply packets in the following format:

  S: 64 bits, `0x3e889045565a9` (magic number for replies)  
  S: 32 bits, the option as sent by the client to which this is a reply  
  S: 32 bits, reply type (e.g., `NBD_REP_ACK` for successful completion,
     or `NBD_REP_ERR_UNSUP` to mark use of an option not known by this
     server  
  S: 32 bits, length of the reply. This may be zero for some replies, in
     which case the next field is not sent  
  S: any data as required by the reply (e.g., an export name in the case
     of `NBD_REP_SERVER`)
[/quote]

I wish those words "apart from NBD_OPT_EXPORT_NAME" were not there
for fixed new style protocol - maybe it needs a very-fixed new
style protocol...

As is, if the client connects to a TLS enabled NBD server and then
immediately sends NBD_OPT_EXPORT_NAME, it is not possible for us
to send back NBD_REP_ERR_TLS_REQD as the spec requires that the
server close the connection :-(  For this reason I have made the
qemu NBD client always send NBD_OPT_LIST as the first thing it
does, so that we can see the NBD_REP_ERR_TLS_REQD response.


I chose to *NOT* implement the NBD_OPT_PEEK_EXPORT option as it is
inherently insecure to have a client to ask the server which
exports need TLS, while the protocol is still running in plain text
mode, as it allows a trivial MITM downgrade attack. I can see value
in the NBD_OPT_PEEK_EXPORT option for probing other features, but
only after TLS has been negotiated. As such I believe NBD_F_EXP_TLS_OK
and NBD_F_EXP_TLS_REQ should be deleted from the spec as it is not
possible to use them in a secure manner.

This series of patches has my v3 I/O channels patch series as a
pre-requisite:

  https://lists.gnu.org/archive/html/qemu-devel/2015-11/msg04208.html

Usage of TLS is described in the commit messages for each patch,
but for sake of people who don't want to explore the series, here's
the summary

Starting QEMU system emulator with a disk backed by an TLS encrypted
NBD export

  $ qemu-system-x86_64 \
     -object tls-creds-x509,id=tls0,endpoint=client,dir=/home/berrange/security/qemutls
     -drive driver=nbd,host=localhost,port=9000,tls-creds=tls0

Starting a standalone NBD server providing a TLS encrypted NBD export

  $ qemu-nbd \
     --object tls-creds-x509,id=tls0,endpoint=server,dir=/home/berrange/security/qemutls
     --tls-creds tls0 \
     --exportname default

NB, exportname is mandatory, since TLS requires the fixed-new style
NBD protocol negotiation.

Starting a QEMU system emulator built-in NBD server

  $ qemu-system-x86_64 \
     -qmp unix:/tmp/qmp,server \
     -hda /home/berrange/Fedora-Server-netinst-x86_64-23.iso \
     -object tls-creds-x509,id=tls0,dir=/home/berrange/security/qemutls,endpoint=server

  $ qmp-shell /tmp/qmp
  (qmp) nbd-server-start addr={"host":"localhost","port":"9000"} tls-creds=tls0
  (qmp) nbd-server-add device=ide0-hd0

The first 6 patches are the conversion to the I/O channels
framework.

The next 5 patches are general tweaks to QEMU's impl of the
NBD protocol for better compliance and/or future proofing.

The next patch provides the NBD protocol TLS implementation.

The final 3 patches allow TLS to be enabled in the QEMU NBD
client and servers.


Daniel P. Berrange (15):
  qom: add user_creatable_add & user_creatable_del methods
  qemu-nbd: add support for --object command line arg
  nbd: convert block client to use I/O channels for connection setup
  nbd: convert qemu-nbd server to use I/O channels for connection setup
  nbd: convert blockdev NBD server to use I/O channels for connection
    setup
  nbd: convert to using I/O channels for actual socket I/O
  nbd: invert client logic for negotiating protocol version
  nbd: make server compliant with fixed newstyle spec
  nbd: make client request fixed new style if advertized
  nbd: allow setting of an export name for qemu-nbd server
  nbd: pick first exported volume if no export name is requested
  nbd: implement TLS support in the protocol negotiation
  nbd: enable use of TLS with NBD block driver
  nbd: enable use of TLS with qemu-nbd server
  nbd: enable use of TLS with nbd-server-start command

 Makefile                        |   6 +-
 block/nbd-client.c              |  74 ++--
 block/nbd-client.h              |  10 +-
 block/nbd.c                     | 105 ++++-
 blockdev-nbd.c                  | 132 +++++-
 hmp.c                           |  13 +-
 include/block/nbd.h             |  26 +-
 include/monitor/monitor.h       |   3 -
 include/qom/object_interfaces.h |  31 ++
 nbd.c                           | 890 ++++++++++++++++++++++++++++++++--------
 qapi/block.json                 |   4 +-
 qemu-nbd.c                      | 242 +++++++++--
 qemu-nbd.texi                   |  14 +
 qmp-commands.hx                 |   2 +-
 qmp.c                           |  75 +---
 qom/object_interfaces.c         |  76 ++++
 tests/Makefile                  |   2 +-
 vl.c                            |   8 +-
 18 files changed, 1333 insertions(+), 380 deletions(-)

-- 
2.5.0

^ permalink raw reply	[flat|nested] 23+ messages in thread

* [Qemu-devel] [PATCH 01/15] qom: add user_creatable_add & user_creatable_del methods
  2015-11-27 12:20 [Qemu-devel] [PATCH 00/15] Implement TLS support to QEMU NBD server & client Daniel P. Berrange
@ 2015-11-27 12:20 ` Daniel P. Berrange
  2015-11-27 12:20 ` [Qemu-devel] [PATCH 02/15] qemu-nbd: add support for --object command line arg Daniel P. Berrange
                   ` (15 subsequent siblings)
  16 siblings, 0 replies; 23+ messages in thread
From: Daniel P. Berrange @ 2015-11-27 12:20 UTC (permalink / raw)
  To: qemu-devel; +Cc: Paolo Bonzini, qemu-block, Wouter Verhelst

The QMP monitor code has two helper methods object_add
and qmp_object_del that are called from several places
in the code (QMP, HMP and main emulator startup).

We soon need to use this code from qemu-img, qemu-io
and qemu-nbd too, but don't want those to depend on
the monitor.

To avoid this, move object_add to user_creatable_add
an qmp_object_del to user_creatable_del, in the
object_interfaces.c file

Signed-off-by: Daniel P. Berrange <berrange@redhat.com>
---
 hmp.c                           | 11 ++++--
 include/monitor/monitor.h       |  3 --
 include/qom/object_interfaces.h | 31 +++++++++++++++++
 qmp.c                           | 75 ++++------------------------------------
 qom/object_interfaces.c         | 76 +++++++++++++++++++++++++++++++++++++++++
 vl.c                            |  8 +++--
 6 files changed, 127 insertions(+), 77 deletions(-)

diff --git a/hmp.c b/hmp.c
index 2140605..6044db3 100644
--- a/hmp.c
+++ b/hmp.c
@@ -29,6 +29,7 @@
 #include "qapi/string-output-visitor.h"
 #include "qapi/util.h"
 #include "qapi-visit.h"
+#include "qom/object_interfaces.h"
 #include "ui/console.h"
 #include "block/qapi.h"
 #include "qemu-io.h"
@@ -1662,6 +1663,7 @@ void hmp_object_add(Monitor *mon, const QDict *qdict)
     void *dummy = NULL;
     OptsVisitor *ov;
     QDict *pdict;
+    Object *obj = NULL;
 
     opts = qemu_opts_from_qdict(qemu_find_opts("object"), qdict, &err);
     if (err) {
@@ -1688,12 +1690,12 @@ void hmp_object_add(Monitor *mon, const QDict *qdict)
         goto out_end;
     }
 
-    object_add(type, id, pdict, opts_get_visitor(ov), &err);
+    obj = user_creatable_add(type, id, pdict, opts_get_visitor(ov), &err);
 
 out_end:
     visit_end_struct(opts_get_visitor(ov), &err_end);
     if (!err && err_end) {
-        qmp_object_del(id, NULL);
+        user_creatable_del(id, NULL);
     }
     error_propagate(&err, err_end);
 out_clean:
@@ -1704,6 +1706,9 @@ out_clean:
     g_free(id);
     g_free(type);
     g_free(dummy);
+    if (obj) {
+        object_unref(obj);
+    }
 
 out:
     hmp_handle_error(mon, &err);
@@ -1936,7 +1941,7 @@ void hmp_object_del(Monitor *mon, const QDict *qdict)
     const char *id = qdict_get_str(qdict, "id");
     Error *err = NULL;
 
-    qmp_object_del(id, &err);
+    user_creatable_del(id, &err);
     hmp_handle_error(mon, &err);
 }
 
diff --git a/include/monitor/monitor.h b/include/monitor/monitor.h
index 91b95ae..aa0f373 100644
--- a/include/monitor/monitor.h
+++ b/include/monitor/monitor.h
@@ -43,9 +43,6 @@ void monitor_read_command(Monitor *mon, int show_prompt);
 int monitor_read_password(Monitor *mon, ReadLineFunc *readline_func,
                           void *opaque);
 
-void object_add(const char *type, const char *id, const QDict *qdict,
-                Visitor *v, Error **errp);
-
 AddfdInfo *monitor_fdset_add_fd(int fd, bool has_fdset_id, int64_t fdset_id,
                                 bool has_opaque, const char *opaque,
                                 Error **errp);
diff --git a/include/qom/object_interfaces.h b/include/qom/object_interfaces.h
index 283ae0d..3e2afeb 100644
--- a/include/qom/object_interfaces.h
+++ b/include/qom/object_interfaces.h
@@ -2,6 +2,8 @@
 #define OBJECT_INTERFACES_H
 
 #include "qom/object.h"
+#include "qapi/qmp/qdict.h"
+#include "qapi/visitor.h"
 
 #define TYPE_USER_CREATABLE "user-creatable"
 
@@ -72,4 +74,33 @@ void user_creatable_complete(Object *obj, Error **errp);
  * from implements USER_CREATABLE interface.
  */
 bool user_creatable_can_be_deleted(UserCreatable *uc, Error **errp);
+
+/**
+ * user_creatable_add:
+ * @type: the object type name
+ * @id: the unique ID for the object
+ * @qdict: the object parameters
+ * @v: the visitor
+ * @errp: if an error occurs, a pointer to an area to store the error
+ *
+ * Create an instance of the user creatable object @type, placing
+ * it in the object composition tree with name @id, initializing
+ * it with properties from @qdict
+ *
+ * Returns: the newly created object or NULL on error
+ */
+Object *user_creatable_add(const char *type, const char *id,
+                           const QDict *qdict,
+                           Visitor *v, Error **errp);
+
+/**
+ * user_creatable_del:
+ * @id: the unique ID for the object
+ * @errp: if an error occurs, a pointer to an area to store the error
+ *
+ * Delete an instance of the user creatable object identified
+ * by @id.
+ */
+void user_creatable_del(const char *id, Error **errp);
+
 #endif
diff --git a/qmp.c b/qmp.c
index 0a1fa19..70fdecd 100644
--- a/qmp.c
+++ b/qmp.c
@@ -622,65 +622,13 @@ void qmp_add_client(const char *protocol, const char *fdname,
     close(fd);
 }
 
-void object_add(const char *type, const char *id, const QDict *qdict,
-                Visitor *v, Error **errp)
-{
-    Object *obj;
-    ObjectClass *klass;
-    const QDictEntry *e;
-    Error *local_err = NULL;
-
-    klass = object_class_by_name(type);
-    if (!klass) {
-        error_setg(errp, "invalid object type: %s", type);
-        return;
-    }
-
-    if (!object_class_dynamic_cast(klass, TYPE_USER_CREATABLE)) {
-        error_setg(errp, "object type '%s' isn't supported by object-add",
-                   type);
-        return;
-    }
-
-    if (object_class_is_abstract(klass)) {
-        error_setg(errp, "object type '%s' is abstract", type);
-        return;
-    }
-
-    obj = object_new(type);
-    if (qdict) {
-        for (e = qdict_first(qdict); e; e = qdict_next(qdict, e)) {
-            object_property_set(obj, v, e->key, &local_err);
-            if (local_err) {
-                goto out;
-            }
-        }
-    }
-
-    object_property_add_child(object_get_objects_root(),
-                              id, obj, &local_err);
-    if (local_err) {
-        goto out;
-    }
-
-    user_creatable_complete(obj, &local_err);
-    if (local_err) {
-        object_property_del(object_get_objects_root(),
-                            id, &error_abort);
-        goto out;
-    }
-out:
-    if (local_err) {
-        error_propagate(errp, local_err);
-    }
-    object_unref(obj);
-}
 
 void qmp_object_add(const char *type, const char *id,
                     bool has_props, QObject *props, Error **errp)
 {
     const QDict *pdict = NULL;
     QmpInputVisitor *qiv;
+    Object *obj;
 
     if (props) {
         pdict = qobject_to_qdict(props);
@@ -691,27 +639,16 @@ void qmp_object_add(const char *type, const char *id,
     }
 
     qiv = qmp_input_visitor_new(props);
-    object_add(type, id, pdict, qmp_input_get_visitor(qiv), errp);
+    obj = user_creatable_add(type, id, pdict, qmp_input_get_visitor(qiv), errp);
     qmp_input_visitor_cleanup(qiv);
+    if (obj) {
+        object_unref(obj);
+    }
 }
 
 void qmp_object_del(const char *id, Error **errp)
 {
-    Object *container;
-    Object *obj;
-
-    container = object_get_objects_root();
-    obj = object_resolve_path_component(container, id);
-    if (!obj) {
-        error_setg(errp, "object id not found");
-        return;
-    }
-
-    if (!user_creatable_can_be_deleted(USER_CREATABLE(obj), errp)) {
-        error_setg(errp, "%s is in use, can not be deleted", id);
-        return;
-    }
-    object_unparent(obj);
+    user_creatable_del(id, errp);
 }
 
 MemoryDeviceInfoList *qmp_query_memory_devices(Error **errp)
diff --git a/qom/object_interfaces.c b/qom/object_interfaces.c
index a66cd60..d94995f 100644
--- a/qom/object_interfaces.c
+++ b/qom/object_interfaces.c
@@ -30,6 +30,82 @@ bool user_creatable_can_be_deleted(UserCreatable *uc, Error **errp)
     }
 }
 
+Object *user_creatable_add(const char *type, const char *id,
+                           const QDict *qdict,
+                           Visitor *v, Error **errp)
+{
+    Object *obj;
+    ObjectClass *klass;
+    const QDictEntry *e;
+    Error *local_err = NULL;
+
+    klass = object_class_by_name(type);
+    if (!klass) {
+        error_setg(errp, "invalid object type: %s", type);
+        return NULL;
+    }
+
+    if (!object_class_dynamic_cast(klass, TYPE_USER_CREATABLE)) {
+        error_setg(errp, "object type '%s' isn't supported by object-add",
+                   type);
+        return NULL;
+    }
+
+    if (object_class_is_abstract(klass)) {
+        error_setg(errp, "object type '%s' is abstract", type);
+        return NULL;
+    }
+
+    obj = object_new(type);
+    if (qdict) {
+        for (e = qdict_first(qdict); e; e = qdict_next(qdict, e)) {
+            object_property_set(obj, v, e->key, &local_err);
+            if (local_err) {
+                goto out;
+            }
+        }
+    }
+
+    object_property_add_child(object_get_objects_root(),
+                              id, obj, &local_err);
+    if (local_err) {
+        goto out;
+    }
+
+    user_creatable_complete(obj, &local_err);
+    if (local_err) {
+        object_property_del(object_get_objects_root(),
+                            id, &error_abort);
+        goto out;
+    }
+out:
+    if (local_err) {
+        error_propagate(errp, local_err);
+        object_unref(obj);
+        return NULL;
+    }
+    return obj;
+}
+
+void user_creatable_del(const char *id, Error **errp)
+{
+    Object *container;
+    Object *obj;
+
+    container = object_get_objects_root();
+    obj = object_resolve_path_component(container, id);
+    if (!obj) {
+        error_setg(errp, "object id not found");
+        return;
+    }
+
+    if (!user_creatable_can_be_deleted(USER_CREATABLE(obj), errp)) {
+        error_setg(errp, "%s is in use, can not be deleted", id);
+        return;
+    }
+    object_unparent(obj);
+}
+
 static void register_types(void)
 {
     static const TypeInfo uc_interface_info = {
diff --git a/vl.c b/vl.c
index 525929b..1f57aa3 100644
--- a/vl.c
+++ b/vl.c
@@ -2834,6 +2834,7 @@ static int object_create(void *opaque, QemuOpts *opts, Error **errp)
     OptsVisitor *ov;
     QDict *pdict;
     bool (*type_predicate)(const char *) = opaque;
+    Object *obj = NULL;
 
     ov = opts_visitor_new(opts);
     pdict = qemu_opts_to_qdict(opts, NULL);
@@ -2858,13 +2859,13 @@ static int object_create(void *opaque, QemuOpts *opts, Error **errp)
         goto out;
     }
 
-    object_add(type, id, pdict, opts_get_visitor(ov), &err);
+    obj = user_creatable_add(type, id, pdict, opts_get_visitor(ov), &err);
     if (err) {
         goto out;
     }
     visit_end_struct(opts_get_visitor(ov), &err);
     if (err) {
-        qmp_object_del(id, NULL);
+        user_creatable_del(id, NULL);
     }
 
 out:
@@ -2874,6 +2875,9 @@ out:
     g_free(id);
     g_free(type);
     g_free(dummy);
+    if (obj) {
+        object_unref(obj);
+    }
     if (err) {
         error_report_err(err);
         return -1;
-- 
2.5.0

^ permalink raw reply related	[flat|nested] 23+ messages in thread

* [Qemu-devel] [PATCH 02/15] qemu-nbd: add support for --object command line arg
  2015-11-27 12:20 [Qemu-devel] [PATCH 00/15] Implement TLS support to QEMU NBD server & client Daniel P. Berrange
  2015-11-27 12:20 ` [Qemu-devel] [PATCH 01/15] qom: add user_creatable_add & user_creatable_del methods Daniel P. Berrange
@ 2015-11-27 12:20 ` Daniel P. Berrange
  2015-11-27 12:20 ` [Qemu-devel] [PATCH 03/15] nbd: convert block client to use I/O channels for connection setup Daniel P. Berrange
                   ` (14 subsequent siblings)
  16 siblings, 0 replies; 23+ messages in thread
From: Daniel P. Berrange @ 2015-11-27 12:20 UTC (permalink / raw)
  To: qemu-devel; +Cc: Paolo Bonzini, qemu-block, Wouter Verhelst

Allow creation of user creatable object types with qemu-nbd
via a --object command line arg. This will be used to supply
passwords and/or encryption keys to the various block driver
backends via the recently added 'secret' object type.

 # echo -n letmein > mypasswd.txt
 # qemu-nbd --object secret,id=sec0,file=mypasswd.txt \
      ...other nbd args...

Signed-off-by: Daniel P. Berrange <berrange@redhat.com>
---
 qemu-nbd.c    | 85 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
 qemu-nbd.texi |  7 +++++
 2 files changed, 92 insertions(+)

diff --git a/qemu-nbd.c b/qemu-nbd.c
index 3afec76..41f4285 100644
--- a/qemu-nbd.c
+++ b/qemu-nbd.c
@@ -23,9 +23,12 @@
 #include "qemu/main-loop.h"
 #include "qemu/sockets.h"
 #include "qemu/error-report.h"
+#include "qemu/config-file.h"
 #include "block/snapshot.h"
 #include "qapi/util.h"
 #include "qapi/qmp/qstring.h"
+#include "qapi/opts-visitor.h"
+#include "qom/object_interfaces.h"
 
 #include <stdarg.h>
 #include <stdio.h>
@@ -45,6 +48,7 @@
 #define QEMU_NBD_OPT_AIO           2
 #define QEMU_NBD_OPT_DISCARD       3
 #define QEMU_NBD_OPT_DETECT_ZEROES 4
+#define QEMU_NBD_OPT_OBJECT        5
 
 static NBDExport *exp;
 static int verbose;
@@ -78,6 +82,9 @@ static void usage(const char *name)
 "  -o, --offset=OFFSET       offset into the image\n"
 "  -P, --partition=NUM       only expose partition NUM\n"
 "\n"
+"General purpose options:\n"
+"  --object type,id=ID,...   define an object such as 'secret' for providing\n"
+"                            passwords and/or encryption keys\n"
 #ifdef __linux__
 "Kernel NBD client support:\n"
 "  -c, --connect=DEV         connect FILE to the local NBD device DEV\n"
@@ -380,6 +387,67 @@ static SocketAddress *nbd_build_socket_address(const char *sockpath,
 }
 
 
+static QemuOptsList qemu_object_opts = {
+    .name = "object",
+    .implied_opt_name = "qom-type",
+    .head = QTAILQ_HEAD_INITIALIZER(qemu_object_opts.head),
+    .desc = {
+        { }
+    },
+};
+
+static int object_create(void *opaque, QemuOpts *opts, Error **errp)
+{
+    Error *err = NULL;
+    char *type = NULL;
+    char *id = NULL;
+    void *dummy = NULL;
+    OptsVisitor *ov;
+    QDict *pdict;
+
+    ov = opts_visitor_new(opts);
+    pdict = qemu_opts_to_qdict(opts, NULL);
+
+    visit_start_struct(opts_get_visitor(ov), &dummy, NULL, NULL, 0, &err);
+    if (err) {
+        goto out;
+    }
+
+    qdict_del(pdict, "qom-type");
+    visit_type_str(opts_get_visitor(ov), &type, "qom-type", &err);
+    if (err) {
+        goto out;
+    }
+
+    qdict_del(pdict, "id");
+    visit_type_str(opts_get_visitor(ov), &id, "id", &err);
+    if (err) {
+        goto out;
+    }
+
+    user_creatable_add(type, id, pdict, opts_get_visitor(ov), &err);
+    if (err) {
+        goto out;
+    }
+    visit_end_struct(opts_get_visitor(ov), &err);
+    if (err) {
+        user_creatable_del(id, NULL);
+    }
+
+out:
+    opts_visitor_cleanup(ov);
+
+    QDECREF(pdict);
+    g_free(id);
+    g_free(type);
+    g_free(dummy);
+    if (err) {
+        error_report_err(err);
+        return -1;
+    }
+    return 0;
+}
+
 int main(int argc, char **argv)
 {
     BlockBackend *blk;
@@ -417,6 +485,7 @@ int main(int argc, char **argv)
         { "format", 1, NULL, 'f' },
         { "persistent", 0, NULL, 't' },
         { "verbose", 0, NULL, 'v' },
+        { "object", 1, NULL, QEMU_NBD_OPT_OBJECT },
         { NULL, 0, NULL, 0 }
     };
     int ch;
@@ -434,6 +503,7 @@ int main(int argc, char **argv)
     Error *local_err = NULL;
     BlockdevDetectZeroesOptions detect_zeroes = BLOCKDEV_DETECT_ZEROES_OPTIONS_OFF;
     QDict *options = NULL;
+    QemuOpts *opts;
 
     /* The client thread uses SIGTERM to interrupt the server.  A signal
      * handler ensures that "qemu-nbd -v -c" exits with a nice status code.
@@ -442,6 +512,8 @@ int main(int argc, char **argv)
     memset(&sa_sigterm, 0, sizeof(sa_sigterm));
     sa_sigterm.sa_handler = termsig_handler;
     sigaction(SIGTERM, &sa_sigterm, NULL);
+    module_call_init(MODULE_INIT_QOM);
+    qemu_add_opts(&qemu_object_opts);
     qemu_init_exec_dir(argv[0]);
 
     while ((ch = getopt_long(argc, argv, sopt, lopt, &opt_ind)) != -1) {
@@ -578,6 +650,13 @@ int main(int argc, char **argv)
             usage(argv[0]);
             exit(0);
             break;
+        case QEMU_NBD_OPT_OBJECT:
+            opts = qemu_opts_parse_noisily(qemu_find_opts("object"),
+                                           optarg, true);
+            if (!opts) {
+                exit(1);
+            }
+            break;
         case '?':
             errx(EXIT_FAILURE, "Try `%s --help' for more information.",
                  argv[0]);
@@ -590,6 +669,12 @@ int main(int argc, char **argv)
              argv[0]);
     }
 
+    if (qemu_opts_foreach(qemu_find_opts("object"),
+                          object_create,
+                          NULL, NULL)) {
+        exit(1);
+    }
+
     if (disconnect) {
         fd = open(argv[optind], O_RDWR);
         if (fd < 0) {
diff --git a/qemu-nbd.texi b/qemu-nbd.texi
index 46fd483..3b2004e 100644
--- a/qemu-nbd.texi
+++ b/qemu-nbd.texi
@@ -14,6 +14,13 @@ Export QEMU disk image using NBD protocol.
 @table @option
 @item @var{filename}
  is a disk image filename
+@item --object type,id=@var{id},...props...
+  define a new instance of the @var{type} object class
+  identified by @var{id}. See the @code{qemu(1)} manual
+  page for full details of the properties supported.
+  The only object type that it makes sense to define
+  is the @code{secret} object, which is used to supply
+  passwords and/or encryption keys.
 @item -p, --port=@var{port}
   port to listen on (default @samp{10809})
 @item -o, --offset=@var{offset}
-- 
2.5.0

^ permalink raw reply related	[flat|nested] 23+ messages in thread

* [Qemu-devel] [PATCH 03/15] nbd: convert block client to use I/O channels for connection setup
  2015-11-27 12:20 [Qemu-devel] [PATCH 00/15] Implement TLS support to QEMU NBD server & client Daniel P. Berrange
  2015-11-27 12:20 ` [Qemu-devel] [PATCH 01/15] qom: add user_creatable_add & user_creatable_del methods Daniel P. Berrange
  2015-11-27 12:20 ` [Qemu-devel] [PATCH 02/15] qemu-nbd: add support for --object command line arg Daniel P. Berrange
@ 2015-11-27 12:20 ` Daniel P. Berrange
  2015-11-27 12:20 ` [Qemu-devel] [PATCH 04/15] nbd: convert qemu-nbd server " Daniel P. Berrange
                   ` (13 subsequent siblings)
  16 siblings, 0 replies; 23+ messages in thread
From: Daniel P. Berrange @ 2015-11-27 12:20 UTC (permalink / raw)
  To: qemu-devel; +Cc: Paolo Bonzini, qemu-block, Wouter Verhelst

This converts the NBD block driver client to use the QIOChannelSocket
class for initial connection setup. The NbdClientSession struct has
two pointers, one to the master QIOChannelSocket providing the raw
data channel, and one to a QIOChannel which is the current channel
used for I/O. Initially the two point to the same object, but when
TLS support is added, they will point to different objects.

In this initial conversion though, all I/O is still actually done
using the raw POSIX sockets APIs.

Signed-off-by: Daniel P. Berrange <berrange@redhat.com>
---
 Makefile           |  6 +++---
 block/nbd-client.c | 59 ++++++++++++++++++++++++++++--------------------------
 block/nbd-client.h |  8 ++++++--
 block/nbd.c        | 39 ++++++++++++++++++------------------
 tests/Makefile     |  2 +-
 5 files changed, 61 insertions(+), 53 deletions(-)

diff --git a/Makefile b/Makefile
index b0049ea..840e99c 100644
--- a/Makefile
+++ b/Makefile
@@ -234,9 +234,9 @@ util/module.o-cflags = -D'CONFIG_BLOCK_MODULES=$(block-modules)'
 
 qemu-img.o: qemu-img-cmds.h
 
-qemu-img$(EXESUF): qemu-img.o $(block-obj-y) $(crypto-obj-y) $(qom-obj-y) libqemuutil.a libqemustub.a
-qemu-nbd$(EXESUF): qemu-nbd.o $(block-obj-y) $(crypto-obj-y) $(qom-obj-y) libqemuutil.a libqemustub.a
-qemu-io$(EXESUF): qemu-io.o $(block-obj-y) $(crypto-obj-y) $(qom-obj-y) libqemuutil.a libqemustub.a
+qemu-img$(EXESUF): qemu-img.o $(block-obj-y) $(crypto-obj-y) $(io-obj-y) $(qom-obj-y) libqemuutil.a libqemustub.a
+qemu-nbd$(EXESUF): qemu-nbd.o $(block-obj-y) $(crypto-obj-y) $(io-obj-y) $(qom-obj-y) libqemuutil.a libqemustub.a
+qemu-io$(EXESUF): qemu-io.o $(block-obj-y) $(crypto-obj-y) $(io-obj-y) $(qom-obj-y) libqemuutil.a libqemustub.a
 
 qemu-bridge-helper$(EXESUF): qemu-bridge-helper.o
 
diff --git a/block/nbd-client.c b/block/nbd-client.c
index b7fd17a..bf5b0a3 100644
--- a/block/nbd-client.c
+++ b/block/nbd-client.c
@@ -27,7 +27,6 @@
  */
 
 #include "nbd-client.h"
-#include "qemu/sockets.h"
 
 #define HANDLE_TO_INDEX(bs, handle) ((handle) ^ ((uint64_t)(intptr_t)bs))
 #define INDEX_TO_HANDLE(bs, index)  ((index)  ^ ((uint64_t)(intptr_t)bs))
@@ -48,12 +47,16 @@ static void nbd_teardown_connection(BlockDriverState *bs)
     NbdClientSession *client = nbd_get_client_session(bs);
 
     /* finish any pending coroutines */
-    shutdown(client->sock, 2);
+    qio_channel_shutdown(client->ioc,
+                         QIO_CHANNEL_SHUTDOWN_BOTH,
+                         NULL);
     nbd_recv_coroutines_enter_all(client);
 
     nbd_client_detach_aio_context(bs);
-    closesocket(client->sock);
-    client->sock = -1;
+    object_unref(OBJECT(client->sioc));
+    client->sioc = NULL;
+    object_unref(OBJECT(client->ioc));
+    client->ioc = NULL;
 }
 
 static void nbd_reply_ready(void *opaque)
@@ -68,7 +71,7 @@ static void nbd_reply_ready(void *opaque)
          * that another thread has done the same thing in parallel, so
          * the socket is not readable anymore.
          */
-        ret = nbd_receive_reply(s->sock, &s->reply);
+        ret = nbd_receive_reply(s->sioc->fd, &s->reply);
         if (ret == -EAGAIN) {
             return;
         }
@@ -124,27 +127,23 @@ static int nbd_co_send_request(BlockDriverState *bs,
     s->send_coroutine = qemu_coroutine_self();
     aio_context = bdrv_get_aio_context(bs);
 
-    aio_set_fd_handler(aio_context, s->sock, false,
+    aio_set_fd_handler(aio_context, s->sioc->fd, false,
                        nbd_reply_ready, nbd_restart_write, bs);
     if (qiov) {
-        if (!s->is_unix) {
-            socket_set_cork(s->sock, 1);
-        }
-        rc = nbd_send_request(s->sock, request);
+        qio_channel_set_cork(s->ioc, true);
+        rc = nbd_send_request(s->sioc->fd, request);
         if (rc >= 0) {
-            ret = qemu_co_sendv(s->sock, qiov->iov, qiov->niov,
+            ret = qemu_co_sendv(s->sioc->fd, qiov->iov, qiov->niov,
                                 offset, request->len);
             if (ret != request->len) {
                 rc = -EIO;
             }
         }
-        if (!s->is_unix) {
-            socket_set_cork(s->sock, 0);
-        }
+        qio_channel_set_cork(s->ioc, false);
     } else {
-        rc = nbd_send_request(s->sock, request);
+        rc = nbd_send_request(s->sioc->fd, request);
     }
-    aio_set_fd_handler(aio_context, s->sock, false,
+    aio_set_fd_handler(aio_context, s->sioc->fd, false,
                        nbd_reply_ready, NULL, bs);
     s->send_coroutine = NULL;
     qemu_co_mutex_unlock(&s->send_mutex);
@@ -165,7 +164,7 @@ static void nbd_co_receive_reply(NbdClientSession *s,
         reply->error = EIO;
     } else {
         if (qiov && reply->error == 0) {
-            ret = qemu_co_recvv(s->sock, qiov->iov, qiov->niov,
+            ret = qemu_co_recvv(s->sioc->fd, qiov->iov, qiov->niov,
                                 offset, request->len);
             if (ret != request->len) {
                 reply->error = EIO;
@@ -349,14 +348,14 @@ int nbd_client_co_discard(BlockDriverState *bs, int64_t sector_num,
 void nbd_client_detach_aio_context(BlockDriverState *bs)
 {
     aio_set_fd_handler(bdrv_get_aio_context(bs),
-                       nbd_get_client_session(bs)->sock,
+                       nbd_get_client_session(bs)->sioc->fd,
                        false, NULL, NULL, NULL);
 }
 
 void nbd_client_attach_aio_context(BlockDriverState *bs,
                                    AioContext *new_context)
 {
-    aio_set_fd_handler(new_context, nbd_get_client_session(bs)->sock,
+    aio_set_fd_handler(new_context, nbd_get_client_session(bs)->sioc->fd,
                        false, nbd_reply_ready, NULL, bs);
 }
 
@@ -369,39 +368,43 @@ void nbd_client_close(BlockDriverState *bs)
         .len = 0
     };
 
-    if (client->sock == -1) {
+    if (client->ioc == NULL) {
         return;
     }
 
-    nbd_send_request(client->sock, &request);
+    nbd_send_request(client->sioc->fd, &request);
 
     nbd_teardown_connection(bs);
 }
 
-int nbd_client_init(BlockDriverState *bs, int sock, const char *export,
-                    Error **errp)
+int nbd_client_init(BlockDriverState *bs, QIOChannelSocket *sioc,
+                    const char *export, Error **errp)
 {
     NbdClientSession *client = nbd_get_client_session(bs);
     int ret;
 
     /* NBD handshake */
     logout("session init %s\n", export);
-    qemu_set_block(sock);
-    ret = nbd_receive_negotiate(sock, export,
+    qio_channel_set_blocking(QIO_CHANNEL(sioc), true, NULL);
+
+    ret = nbd_receive_negotiate(sioc->fd, export,
                                 &client->nbdflags, &client->size, errp);
     if (ret < 0) {
         logout("Failed to negotiate with the NBD server\n");
-        closesocket(sock);
         return ret;
     }
 
     qemu_co_mutex_init(&client->send_mutex);
     qemu_co_mutex_init(&client->free_sema);
-    client->sock = sock;
+    client->sioc = sioc;
+    object_ref(OBJECT(client->sioc));
+    client->ioc = QIO_CHANNEL(sioc);
+    object_ref(OBJECT(client->ioc));
 
     /* Now that we're connected, set the socket to be non-blocking and
      * kick the reply mechanism.  */
-    qemu_set_nonblock(sock);
+    qio_channel_set_blocking(QIO_CHANNEL(sioc), false, NULL);
+
     nbd_client_attach_aio_context(bs, bdrv_get_aio_context(bs));
 
     logout("Established connection with NBD server\n");
diff --git a/block/nbd-client.h b/block/nbd-client.h
index e841340..e8b3283 100644
--- a/block/nbd-client.h
+++ b/block/nbd-client.h
@@ -4,6 +4,7 @@
 #include "qemu-common.h"
 #include "block/nbd.h"
 #include "block/block_int.h"
+#include "io/channel-socket.h"
 
 /* #define DEBUG_NBD */
 
@@ -17,7 +18,8 @@
 #define MAX_NBD_REQUESTS    16
 
 typedef struct NbdClientSession {
-    int sock;
+    QIOChannelSocket *sioc; /* The master data channel */
+    QIOChannel *ioc; /* The current I/O channel which may differ (eg TLS) */
     uint32_t nbdflags;
     off_t size;
 
@@ -34,7 +36,9 @@ typedef struct NbdClientSession {
 
 NbdClientSession *nbd_get_client_session(BlockDriverState *bs);
 
-int nbd_client_init(BlockDriverState *bs, int sock, const char *export_name,
+int nbd_client_init(BlockDriverState *bs,
+                    QIOChannelSocket *sock,
+                    const char *export_name,
                     Error **errp);
 void nbd_client_close(BlockDriverState *bs);
 
diff --git a/block/nbd.c b/block/nbd.c
index cd6a587..e51acfc 100644
--- a/block/nbd.c
+++ b/block/nbd.c
@@ -30,7 +30,6 @@
 #include "qemu/uri.h"
 #include "block/block_int.h"
 #include "qemu/module.h"
-#include "qemu/sockets.h"
 #include "qapi/qmp/qdict.h"
 #include "qapi/qmp/qjson.h"
 #include "qapi/qmp/qint.h"
@@ -239,25 +238,25 @@ NbdClientSession *nbd_get_client_session(BlockDriverState *bs)
     return &s->client;
 }
 
-static int nbd_establish_connection(BlockDriverState *bs,
-                                    SocketAddress *saddr,
-                                    Error **errp)
+static QIOChannelSocket *nbd_establish_connection(SocketAddress *saddr,
+                                                  Error **errp)
 {
-    BDRVNBDState *s = bs->opaque;
-    int sock;
+    QIOChannelSocket *sioc;
+    Error *local_err = NULL;
 
-    sock = socket_connect(saddr, errp, NULL, NULL);
+    sioc = qio_channel_socket_new();
 
-    if (sock < 0) {
-        logout("Failed to establish connection to NBD server\n");
-        return -EIO;
+    qio_channel_socket_connect_sync(sioc,
+                                    saddr,
+                                    &local_err);
+    if (local_err) {
+        error_propagate(errp, local_err);
+        return NULL;
     }
 
-    if (!s->client.is_unix) {
-        socket_set_nodelay(sock);
-    }
+    qio_channel_set_delay(QIO_CHANNEL(sioc), false);
 
-    return sock;
+    return sioc;
 }
 
 static int nbd_open(BlockDriverState *bs, QDict *options, int flags,
@@ -265,7 +264,8 @@ static int nbd_open(BlockDriverState *bs, QDict *options, int flags,
 {
     BDRVNBDState *s = bs->opaque;
     char *export = NULL;
-    int result, sock;
+    int result;
+    QIOChannelSocket *sioc;
     SocketAddress *saddr;
 
     /* Pop the config into our state object. Exit if invalid. */
@@ -277,15 +277,16 @@ static int nbd_open(BlockDriverState *bs, QDict *options, int flags,
     /* establish TCP connection, return error if it fails
      * TODO: Configurable retry-until-timeout behaviour.
      */
-    sock = nbd_establish_connection(bs, saddr, errp);
+    sioc = nbd_establish_connection(saddr, errp);
     qapi_free_SocketAddress(saddr);
-    if (sock < 0) {
+    if (!sioc) {
         g_free(export);
-        return sock;
+        return -ECONNREFUSED;
     }
 
     /* NBD handshake */
-    result = nbd_client_init(bs, sock, export, errp);
+    result = nbd_client_init(bs, sioc, export, errp);
+    object_unref(OBJECT(sioc));
     g_free(export);
     return result;
 }
diff --git a/tests/Makefile b/tests/Makefile
index c0c9de6..d46b21b 100644
--- a/tests/Makefile
+++ b/tests/Makefile
@@ -386,8 +386,8 @@ test-qapi-obj-y = tests/test-qapi-visit.o tests/test-qapi-types.o \
 	tests/test-qapi-event.o tests/test-qmp-introspect.o \
 	$(test-qom-obj-y)
 test-crypto-obj-y = $(crypto-obj-y) $(test-qom-obj-y)
-test-block-obj-y = $(block-obj-y) $(test-crypto-obj-y)
 test-io-obj-y = $(io-obj-y) $(test-crypto-obj-y)
+test-block-obj-y = $(block-obj-y) $(test-io-obj-y)
 
 tests/check-qint$(EXESUF): tests/check-qint.o $(test-util-obj-y)
 tests/check-qstring$(EXESUF): tests/check-qstring.o $(test-util-obj-y)
-- 
2.5.0

^ permalink raw reply related	[flat|nested] 23+ messages in thread

* [Qemu-devel] [PATCH 04/15] nbd: convert qemu-nbd server to use I/O channels for connection setup
  2015-11-27 12:20 [Qemu-devel] [PATCH 00/15] Implement TLS support to QEMU NBD server & client Daniel P. Berrange
                   ` (2 preceding siblings ...)
  2015-11-27 12:20 ` [Qemu-devel] [PATCH 03/15] nbd: convert block client to use I/O channels for connection setup Daniel P. Berrange
@ 2015-11-27 12:20 ` Daniel P. Berrange
  2015-11-27 12:20 ` [Qemu-devel] [PATCH 05/15] nbd: convert blockdev NBD " Daniel P. Berrange
                   ` (12 subsequent siblings)
  16 siblings, 0 replies; 23+ messages in thread
From: Daniel P. Berrange @ 2015-11-27 12:20 UTC (permalink / raw)
  To: qemu-devel; +Cc: Paolo Bonzini, qemu-block, Wouter Verhelst

This converts the qemu-nbd server to use the QIOChannelSocket
class for initial listener socket setup and accepting of client
connections. Actual I/O is still being performed against the
socket file descriptor using the POSIX socket APIs.

In this initial conversion though, all I/O is still actually done
using the raw POSIX sockets APIs.

Signed-off-by: Daniel P. Berrange <berrange@redhat.com>
---
 qemu-nbd.c | 91 ++++++++++++++++++++++++++++++++++----------------------------
 1 file changed, 50 insertions(+), 41 deletions(-)

diff --git a/qemu-nbd.c b/qemu-nbd.c
index 41f4285..28ab54f 100644
--- a/qemu-nbd.c
+++ b/qemu-nbd.c
@@ -21,7 +21,6 @@
 #include "block/block_int.h"
 #include "block/nbd.h"
 #include "qemu/main-loop.h"
-#include "qemu/sockets.h"
 #include "qemu/error-report.h"
 #include "qemu/config-file.h"
 #include "block/snapshot.h"
@@ -29,16 +28,13 @@
 #include "qapi/qmp/qstring.h"
 #include "qapi/opts-visitor.h"
 #include "qom/object_interfaces.h"
+#include "io/channel-socket.h"
 
 #include <stdarg.h>
 #include <stdio.h>
 #include <getopt.h>
 #include <err.h>
 #include <sys/types.h>
-#include <sys/socket.h>
-#include <netinet/in.h>
-#include <netinet/tcp.h>
-#include <arpa/inet.h>
 #include <signal.h>
 #include <libgen.h>
 #include <pthread.h>
@@ -58,7 +54,8 @@ static int persistent = 0;
 static enum { RUNNING, TERMINATE, TERMINATING, TERMINATED } state;
 static int shared = 1;
 static int nb_fds;
-static int server_fd;
+static QIOChannelSocket *server_ioc;
+static int server_watch = -1;
 
 static void usage(const char *name)
 {
@@ -241,19 +238,21 @@ static void *nbd_client_thread(void *arg)
     char *device = arg;
     off_t size;
     uint32_t nbdflags;
-    int fd, sock;
+    QIOChannelSocket *sioc;
+    int fd;
     int ret;
     pthread_t show_parts_thread;
     Error *local_error = NULL;
 
-
-    sock = socket_connect(saddr, &local_error, NULL, NULL);
-    if (sock < 0) {
+    sioc = qio_channel_socket_new();
+    if (qio_channel_socket_connect_sync(sioc,
+                                        saddr,
+                                        &local_error) < 0) {
         error_report_err(local_error);
         goto out;
     }
 
-    ret = nbd_receive_negotiate(sock, NULL, &nbdflags,
+    ret = nbd_receive_negotiate(sioc->fd, NULL, &nbdflags,
                                 &size, &local_error);
     if (ret < 0) {
         if (local_error) {
@@ -270,7 +269,7 @@ static void *nbd_client_thread(void *arg)
         goto out_socket;
     }
 
-    ret = nbd_init(fd, sock, nbdflags, size);
+    ret = nbd_init(fd, sioc->fd, nbdflags, size);
     if (ret < 0) {
         goto out_fd;
     }
@@ -291,13 +290,14 @@ static void *nbd_client_thread(void *arg)
         goto out_fd;
     }
     close(fd);
+    object_unref(OBJECT(sioc));
     kill(getpid(), SIGTERM);
     return (void *) EXIT_SUCCESS;
 
 out_fd:
     close(fd);
 out_socket:
-    closesocket(sock);
+    object_unref(OBJECT(sioc));
 out:
     kill(getpid(), SIGTERM);
     return (void *) EXIT_FAILURE;
@@ -314,7 +314,7 @@ static void nbd_export_closed(NBDExport *exp)
     state = TERMINATED;
 }
 
-static void nbd_update_server_fd_handler(int fd);
+static void nbd_update_server_watch(void);
 
 static void nbd_client_closed(NBDClient *client)
 {
@@ -322,41 +322,51 @@ static void nbd_client_closed(NBDClient *client)
     if (nb_fds == 0 && !persistent && state == RUNNING) {
         state = TERMINATE;
     }
-    nbd_update_server_fd_handler(server_fd);
+    nbd_update_server_watch();
     nbd_client_put(client);
 }
 
-static void nbd_accept(void *opaque)
+static gboolean nbd_accept(QIOChannel *ioc, GIOCondition cond, gpointer opaque)
 {
-    struct sockaddr_in addr;
-    socklen_t addr_len = sizeof(addr);
+    QIOChannelSocket *cioc;
+    int fd;
 
-    int fd = accept(server_fd, (struct sockaddr *)&addr, &addr_len);
-    if (fd < 0) {
-        perror("accept");
-        return;
+    cioc = qio_channel_socket_accept(QIO_CHANNEL_SOCKET(ioc),
+                                     NULL);
+    if (!cioc) {
+        return TRUE;
     }
 
     if (state >= TERMINATE) {
-        close(fd);
-        return;
+        object_unref(OBJECT(cioc));
+        return TRUE;
     }
 
-    if (nbd_client_new(exp, fd, nbd_client_closed)) {
+    fd = dup(cioc->fd);
+    if (fd >= 0 &&
+        nbd_client_new(exp, fd, nbd_client_closed)) {
         nb_fds++;
-        nbd_update_server_fd_handler(server_fd);
-    } else {
-        shutdown(fd, 2);
-        close(fd);
+        nbd_update_server_watch();
     }
+    object_unref(OBJECT(cioc));
+
+    return TRUE;
 }
 
-static void nbd_update_server_fd_handler(int fd)
+static void nbd_update_server_watch(void)
 {
     if (nbd_can_accept()) {
-        qemu_set_fd_handler(fd, nbd_accept, NULL, (void *)(uintptr_t)fd);
+        if (server_watch == -1) {
+            server_watch = qio_channel_add_watch(QIO_CHANNEL(server_ioc),
+                                                 G_IO_IN,
+                                                 nbd_accept,
+                                                 NULL, NULL);
+        }
     } else {
-        qemu_set_fd_handler(fd, NULL, NULL, NULL);
+        if (server_watch != -1) {
+            g_source_remove(server_watch);
+            server_watch = -1;
+        }
     }
 }
 
@@ -494,7 +504,6 @@ int main(int argc, char **argv)
     int flags = BDRV_O_RDWR;
     int partition = -1;
     int ret = 0;
-    int fd;
     bool seen_cache = false;
     bool seen_discard = false;
     bool seen_aio = false;
@@ -676,13 +685,13 @@ int main(int argc, char **argv)
     }
 
     if (disconnect) {
-        fd = open(argv[optind], O_RDWR);
-        if (fd < 0) {
+        int nbdfd = open(argv[optind], O_RDWR);
+        if (nbdfd < 0) {
             err(EXIT_FAILURE, "Cannot open %s", argv[optind]);
         }
-        nbd_disconnect(fd);
+        nbd_disconnect(nbdfd);
 
-        close(fd);
+        close(nbdfd);
 
         printf("%s disconnected\n", argv[optind]);
 
@@ -807,8 +816,9 @@ int main(int argc, char **argv)
         errx(EXIT_FAILURE, "%s", error_get_pretty(local_err));
     }
 
-    fd = socket_listen(saddr, &local_err);
-    if (fd < 0) {
+    server_ioc = qio_channel_socket_new();
+    if (qio_channel_socket_listen_sync(server_ioc, saddr, &local_err) < 0) {
+        object_unref(OBJECT(server_ioc));
         error_report_err(local_err);
         return 1;
     }
@@ -826,8 +836,7 @@ int main(int argc, char **argv)
         memset(&client_thread, 0, sizeof(client_thread));
     }
 
-    server_fd = fd;
-    nbd_update_server_fd_handler(fd);
+    nbd_update_server_watch();
 
     /* now when the initialization is (almost) complete, chdir("/")
      * to free any busy filesystems */
-- 
2.5.0

^ permalink raw reply related	[flat|nested] 23+ messages in thread

* [Qemu-devel] [PATCH 05/15] nbd: convert blockdev NBD server to use I/O channels for connection setup
  2015-11-27 12:20 [Qemu-devel] [PATCH 00/15] Implement TLS support to QEMU NBD server & client Daniel P. Berrange
                   ` (3 preceding siblings ...)
  2015-11-27 12:20 ` [Qemu-devel] [PATCH 04/15] nbd: convert qemu-nbd server " Daniel P. Berrange
@ 2015-11-27 12:20 ` Daniel P. Berrange
  2015-11-27 12:20 ` [Qemu-devel] [PATCH 06/15] nbd: convert to using I/O channels for actual socket I/O Daniel P. Berrange
                   ` (11 subsequent siblings)
  16 siblings, 0 replies; 23+ messages in thread
From: Daniel P. Berrange @ 2015-11-27 12:20 UTC (permalink / raw)
  To: qemu-devel; +Cc: Paolo Bonzini, qemu-block, Wouter Verhelst

This converts the blockdev NBD server to use the QIOChannelSocket
class for initial listener socket setup and accepting of client
connections. Actual I/O is still being performed against the
socket file descriptor using the POSIX socket APIs.

Signed-off-by: Daniel P. Berrange <berrange@redhat.com>
---
 blockdev-nbd.c | 53 ++++++++++++++++++++++++++++++++++++-----------------
 1 file changed, 36 insertions(+), 17 deletions(-)

diff --git a/blockdev-nbd.c b/blockdev-nbd.c
index bcdd18b..71ee021 100644
--- a/blockdev-nbd.c
+++ b/blockdev-nbd.c
@@ -17,33 +17,49 @@
 #include "qmp-commands.h"
 #include "trace.h"
 #include "block/nbd.h"
-#include "qemu/sockets.h"
+#include "io/channel-socket.h"
 
-static int server_fd = -1;
+static QIOChannelSocket *server_ioc;
+static int server_watch = -1;
 
-static void nbd_accept(void *opaque)
+static gboolean nbd_accept(QIOChannel *ioc, GIOCondition condition,
+                           gpointer opaque)
 {
-    struct sockaddr_in addr;
-    socklen_t addr_len = sizeof(addr);
+    QIOChannelSocket *cioc;
+    int fd;
 
-    int fd = accept(server_fd, (struct sockaddr *)&addr, &addr_len);
-    if (fd >= 0 && !nbd_client_new(NULL, fd, nbd_client_put)) {
-        shutdown(fd, 2);
+    cioc = qio_channel_socket_accept(QIO_CHANNEL_SOCKET(ioc),
+                                     NULL);
+    if (!cioc) {
+        return TRUE;
+    }
+
+    fd = dup(cioc->fd);
+    if (fd >= 0 &&
+        !nbd_client_new(NULL, fd, nbd_client_put)) {
         close(fd);
     }
+    object_unref(OBJECT(cioc));
+    return TRUE;
 }
 
 void qmp_nbd_server_start(SocketAddress *addr, Error **errp)
 {
-    if (server_fd != -1) {
+    if (server_ioc) {
         error_setg(errp, "NBD server already running");
         return;
     }
 
-    server_fd = socket_listen(addr, errp);
-    if (server_fd != -1) {
-        qemu_set_fd_handler(server_fd, nbd_accept, NULL, NULL);
+    server_ioc = qio_channel_socket_new();
+    if (qio_channel_socket_listen_sync(server_ioc, addr, errp) < 0) {
+        return;
     }
+
+    server_watch = qio_channel_add_watch(QIO_CHANNEL(server_ioc),
+                                         G_IO_IN,
+                                         nbd_accept,
+                                         NULL,
+                                         NULL);
 }
 
 /*
@@ -78,7 +94,7 @@ void qmp_nbd_server_add(const char *device, bool has_writable, bool writable,
     NBDExport *exp;
     NBDCloseNotifier *n;
 
-    if (server_fd == -1) {
+    if (!server_ioc) {
         error_setg(errp, "NBD server not running");
         return;
     }
@@ -128,9 +144,12 @@ void qmp_nbd_server_stop(Error **errp)
         nbd_close_notifier(&cn->n, nbd_export_get_blockdev(cn->exp));
     }
 
-    if (server_fd != -1) {
-        qemu_set_fd_handler(server_fd, NULL, NULL, NULL);
-        close(server_fd);
-        server_fd = -1;
+    if (server_watch != -1) {
+        g_source_remove(server_watch);
+        server_watch = -1;
+    }
+    if (server_ioc) {
+        object_unref(OBJECT(server_ioc));
+        server_ioc = NULL;
     }
 }
-- 
2.5.0

^ permalink raw reply related	[flat|nested] 23+ messages in thread

* [Qemu-devel] [PATCH 06/15] nbd: convert to using I/O channels for actual socket I/O
  2015-11-27 12:20 [Qemu-devel] [PATCH 00/15] Implement TLS support to QEMU NBD server & client Daniel P. Berrange
                   ` (4 preceding siblings ...)
  2015-11-27 12:20 ` [Qemu-devel] [PATCH 05/15] nbd: convert blockdev NBD " Daniel P. Berrange
@ 2015-11-27 12:20 ` Daniel P. Berrange
  2015-11-27 12:20 ` [Qemu-devel] [PATCH 07/15] nbd: invert client logic for negotiating protocol version Daniel P. Berrange
                   ` (10 subsequent siblings)
  16 siblings, 0 replies; 23+ messages in thread
From: Daniel P. Berrange @ 2015-11-27 12:20 UTC (permalink / raw)
  To: qemu-devel; +Cc: Paolo Bonzini, qemu-block, Wouter Verhelst

Now that all callers are converted to use I/O channels for
initial connection setup, it is possible to switch the core
NBD protocol handling core over to use QIOChannel APIs for
acutal sockets I/O.

Signed-off-by: Daniel P. Berrange <berrange@redhat.com>
---
 block/nbd-client.c  |  19 ++--
 blockdev-nbd.c      |   7 +-
 include/block/nbd.h |  19 ++--
 nbd.c               | 289 ++++++++++++++++++++++++++++++++--------------------
 qemu-nbd.c          |   9 +-
 5 files changed, 205 insertions(+), 138 deletions(-)

diff --git a/block/nbd-client.c b/block/nbd-client.c
index bf5b0a3..ed69d4b 100644
--- a/block/nbd-client.c
+++ b/block/nbd-client.c
@@ -71,7 +71,7 @@ static void nbd_reply_ready(void *opaque)
          * that another thread has done the same thing in parallel, so
          * the socket is not readable anymore.
          */
-        ret = nbd_receive_reply(s->sioc->fd, &s->reply);
+        ret = nbd_receive_reply(s->ioc, &s->reply);
         if (ret == -EAGAIN) {
             return;
         }
@@ -122,6 +122,7 @@ static int nbd_co_send_request(BlockDriverState *bs,
         }
     }
 
+    g_assert(qemu_in_coroutine());
     assert(i < MAX_NBD_REQUESTS);
     request->handle = INDEX_TO_HANDLE(s, i);
     s->send_coroutine = qemu_coroutine_self();
@@ -131,17 +132,17 @@ static int nbd_co_send_request(BlockDriverState *bs,
                        nbd_reply_ready, nbd_restart_write, bs);
     if (qiov) {
         qio_channel_set_cork(s->ioc, true);
-        rc = nbd_send_request(s->sioc->fd, request);
+        rc = nbd_send_request(s->ioc, request);
         if (rc >= 0) {
-            ret = qemu_co_sendv(s->sioc->fd, qiov->iov, qiov->niov,
-                                offset, request->len);
+            ret = nbd_wr_syncv(s->ioc, qiov->iov, qiov->niov,
+                               offset, request->len, 0);
             if (ret != request->len) {
                 rc = -EIO;
             }
         }
         qio_channel_set_cork(s->ioc, false);
     } else {
-        rc = nbd_send_request(s->sioc->fd, request);
+        rc = nbd_send_request(s->ioc, request);
     }
     aio_set_fd_handler(aio_context, s->sioc->fd, false,
                        nbd_reply_ready, NULL, bs);
@@ -164,8 +165,8 @@ static void nbd_co_receive_reply(NbdClientSession *s,
         reply->error = EIO;
     } else {
         if (qiov && reply->error == 0) {
-            ret = qemu_co_recvv(s->sioc->fd, qiov->iov, qiov->niov,
-                                offset, request->len);
+            ret = nbd_wr_syncv(s->ioc, qiov->iov, qiov->niov,
+                               offset, request->len, 1);
             if (ret != request->len) {
                 reply->error = EIO;
             }
@@ -372,7 +373,7 @@ void nbd_client_close(BlockDriverState *bs)
         return;
     }
 
-    nbd_send_request(client->sioc->fd, &request);
+    nbd_send_request(client->ioc, &request);
 
     nbd_teardown_connection(bs);
 }
@@ -387,7 +388,7 @@ int nbd_client_init(BlockDriverState *bs, QIOChannelSocket *sioc,
     logout("session init %s\n", export);
     qio_channel_set_blocking(QIO_CHANNEL(sioc), true, NULL);
 
-    ret = nbd_receive_negotiate(sioc->fd, export,
+    ret = nbd_receive_negotiate(QIO_CHANNEL(sioc), export,
                                 &client->nbdflags, &client->size, errp);
     if (ret < 0) {
         logout("Failed to negotiate with the NBD server\n");
diff --git a/blockdev-nbd.c b/blockdev-nbd.c
index 71ee021..7d4c415 100644
--- a/blockdev-nbd.c
+++ b/blockdev-nbd.c
@@ -26,7 +26,6 @@ static gboolean nbd_accept(QIOChannel *ioc, GIOCondition condition,
                            gpointer opaque)
 {
     QIOChannelSocket *cioc;
-    int fd;
 
     cioc = qio_channel_socket_accept(QIO_CHANNEL_SOCKET(ioc),
                                      NULL);
@@ -34,11 +33,7 @@ static gboolean nbd_accept(QIOChannel *ioc, GIOCondition condition,
         return TRUE;
     }
 
-    fd = dup(cioc->fd);
-    if (fd >= 0 &&
-        !nbd_client_new(NULL, fd, nbd_client_put)) {
-        close(fd);
-    }
+    nbd_client_new(NULL, cioc, nbd_client_put);
     object_unref(OBJECT(cioc));
     return TRUE;
 }
diff --git a/include/block/nbd.h b/include/block/nbd.h
index 65f409d..0230c7f 100644
--- a/include/block/nbd.h
+++ b/include/block/nbd.h
@@ -23,6 +23,7 @@
 
 #include "qemu-common.h"
 #include "qemu/option.h"
+#include "io/channel-socket.h"
 
 struct nbd_request {
     uint32_t magic;
@@ -73,12 +74,17 @@ enum {
 /* Maximum size of a single READ/WRITE data buffer */
 #define NBD_MAX_BUFFER_SIZE (32 * 1024 * 1024)
 
-ssize_t nbd_wr_sync(int fd, void *buffer, size_t size, bool do_read);
-int nbd_receive_negotiate(int csock, const char *name, uint32_t *flags,
+ssize_t nbd_wr_syncv(QIOChannel *ioc,
+                     struct iovec *iov,
+                     size_t niov,
+                     size_t offset,
+                     size_t length,
+                     bool do_read);
+int nbd_receive_negotiate(QIOChannel *ioc, const char *name, uint32_t *flags,
                           off_t *size, Error **errp);
-int nbd_init(int fd, int csock, uint32_t flags, off_t size);
-ssize_t nbd_send_request(int csock, struct nbd_request *request);
-ssize_t nbd_receive_reply(int csock, struct nbd_reply *reply);
+int nbd_init(int fd, QIOChannelSocket *sioc, uint32_t flags, off_t size);
+ssize_t nbd_send_request(QIOChannel *ioc, struct nbd_request *request);
+ssize_t nbd_receive_reply(QIOChannel *ioc, struct nbd_reply *reply);
 int nbd_client(int fd);
 int nbd_disconnect(int fd);
 
@@ -98,7 +104,8 @@ NBDExport *nbd_export_find(const char *name);
 void nbd_export_set_name(NBDExport *exp, const char *name);
 void nbd_export_close_all(void);
 
-NBDClient *nbd_client_new(NBDExport *exp, int csock,
+NBDClient *nbd_client_new(NBDExport *exp,
+                          QIOChannelSocket *sioc,
                           void (*close)(NBDClient *));
 void nbd_client_get(NBDClient *client);
 void nbd_client_put(NBDClient *client);
diff --git a/nbd.c b/nbd.c
index b3d9654..701b951 100644
--- a/nbd.c
+++ b/nbd.c
@@ -20,6 +20,7 @@
 #include "sysemu/block-backend.h"
 
 #include "qemu/coroutine.h"
+#include "qemu/iov.h"
 
 #include <errno.h>
 #include <string.h>
@@ -36,7 +37,6 @@
 #include <linux/fs.h>
 #endif
 
-#include "qemu/sockets.h"
 #include "qemu/queue.h"
 #include "qemu/main-loop.h"
 
@@ -171,7 +171,8 @@ struct NBDClient {
     void (*close)(NBDClient *client);
 
     NBDExport *exp;
-    int sock;
+    QIOChannelSocket *sioc; /* The underlying data channel */
+    QIOChannel *ioc; /* The current I/O channel which may differ (eg TLS) */
 
     Coroutine *recv_coroutine;
 
@@ -191,68 +192,127 @@ static void nbd_set_handlers(NBDClient *client);
 static void nbd_unset_handlers(NBDClient *client);
 static void nbd_update_can_read(NBDClient *client);
 
-ssize_t nbd_wr_sync(int fd, void *buffer, size_t size, bool do_read)
+/**
+ * @skip: number of bytes to advance head of @iov
+ * @iov: pointer to iov array, updated on success
+ * @niov: number of elements in @iov, updated on success
+ * @oldiov: pointer single element to hold old info from @iov
+ *
+ * This will update @iov so that its head is advanced
+ * by @skip bytes. To do this, zero or more complete
+ * elements of @iov will be skipped over. The new head
+ * of @iov will then have its base & len updated to
+ * skip the remaining number of bytes. @oldiov will
+ * hold the original data from the new head of @iov.
+ */
+static void nbd_skip_iov(size_t skip,
+                         struct iovec **iov,
+                         int *niov,
+                         struct iovec *oldiov)
 {
-    size_t offset = 0;
-    int err;
-
-    if (qemu_in_coroutine()) {
-        if (do_read) {
-            return qemu_co_recv(fd, buffer, size);
+    ssize_t done = 0;
+    size_t i;
+    for (i = 0; i < *niov; i++) {
+        if ((*iov)[i].iov_len <= skip) {
+            done += (*iov)[i].iov_len;
+            skip -= (*iov)[i].iov_len;
         } else {
-            return qemu_co_send(fd, buffer, size);
+            done += skip;
+            oldiov->iov_base = (*iov)[i].iov_base;
+            oldiov->iov_len = (*iov)[i].iov_len;
+            (*iov)[i].iov_len -= skip;
+            (*iov)[i].iov_base += skip;
+            *iov = *iov + i;
+            *niov = *niov - i;
+            break;
         }
     }
+}
+
+ssize_t nbd_wr_syncv(QIOChannel *ioc,
+                     struct iovec *iov,
+                     size_t niov,
+                     size_t offset,
+                     size_t length,
+                     bool do_read)
+{
+    ssize_t done = 0;
+    size_t iovlen = iov_size(iov, niov);
+    struct iovec oldiov = { NULL, 0 };
+
+    /* Shouldn't happen but just to be sure */
+    if (offset > iovlen) {
+        return -EINVAL;
+    }
+    iovlen -= offset;
+    if (length > iovlen) {
+        return -EINVAL;
+    }
 
-    while (offset < size) {
+    while (done < length) {
         ssize_t len;
+        struct iovec *cur = iov;
+        int curcnt = niov;
+
+        nbd_skip_iov(offset + done, &cur, &curcnt, &oldiov);
 
         if (do_read) {
-            len = qemu_recv(fd, buffer + offset, size - offset, 0);
+            len = qio_channel_readv(ioc, cur, curcnt, NULL);
         } else {
-            len = send(fd, buffer + offset, size - offset, 0);
+            len = qio_channel_writev(ioc, cur, curcnt, NULL);
         }
-
-        if (len < 0) {
-            err = socket_error();
-
-            /* recoverable error */
-            if (err == EINTR || (offset > 0 && (err == EAGAIN || err == EWOULDBLOCK))) {
-                continue;
+        if (oldiov.iov_base) {
+            /* Restore original caller's info in @iov */
+            cur[0].iov_base = oldiov.iov_base;
+            cur[0].iov_len = oldiov.iov_len;
+            oldiov.iov_base = NULL;
+            oldiov.iov_len = 0;
+        }
+        if (len == QIO_CHANNEL_ERR_BLOCK) {
+            if (qemu_in_coroutine()) {
+                /* XXX see about using qio_channel_yield() in
+                 * future if we can use normal watches instead
+                 * of the AIO handlers */
+                qemu_coroutine_yield();
+            } else {
+                qio_channel_wait(ioc,
+                                 do_read ? G_IO_IN : G_IO_OUT);
             }
-
-            /* unrecoverable error */
-            return -err;
+            continue;
+        }
+        if (len < 0) {
+            /* XXX handle Error objects */
+            return -EIO;
         }
 
-        /* eof */
-        if (len == 0) {
+        if (do_read && len == 0) {
             break;
         }
 
-        offset += len;
+        done += len;
     }
-
-    return offset;
+    return done;
 }
 
-static ssize_t read_sync(int fd, void *buffer, size_t size)
+
+static ssize_t read_sync(QIOChannel *ioc, void *buffer, size_t size)
 {
+    struct iovec iov = { .iov_base = buffer, .iov_len = size };
     /* Sockets are kept in blocking mode in the negotiation phase.  After
      * that, a non-readable socket simply means that another thread stole
      * our request/reply.  Synchronization is done with recv_coroutine, so
      * that this is coroutine-safe.
      */
-    return nbd_wr_sync(fd, buffer, size, true);
+    return nbd_wr_syncv(ioc, &iov, 1, 0, size, true);
 }
 
-static ssize_t drop_sync(int fd, size_t size)
+static ssize_t drop_sync(QIOChannel *ioc, size_t size)
 {
     ssize_t ret, dropped = size;
     uint8_t *buffer = g_malloc(MIN(65536, size));
 
     while (size > 0) {
-        ret = read_sync(fd, buffer, MIN(65536, size));
+        ret = read_sync(ioc, buffer, MIN(65536, size));
         if (ret < 0) {
             g_free(buffer);
             return ret;
@@ -266,14 +326,11 @@ static ssize_t drop_sync(int fd, size_t size)
     return dropped;
 }
 
-static ssize_t write_sync(int fd, void *buffer, size_t size)
+static ssize_t write_sync(QIOChannel *ioc, void *buffer, size_t size)
 {
-    int ret;
-    do {
-        /* For writes, we do expect the socket to be writable.  */
-        ret = nbd_wr_sync(fd, buffer, size, false);
-    } while (ret == -EAGAIN);
-    return ret;
+    struct iovec iov = { .iov_base = buffer, .iov_len = size };
+
+    return nbd_wr_syncv(ioc, &iov, 1, 0, size, false);
 }
 
 /* Basic flow for negotiation
@@ -303,66 +360,66 @@ static ssize_t write_sync(int fd, void *buffer, size_t size)
 
 */
 
-static int nbd_send_rep(int csock, uint32_t type, uint32_t opt)
+static int nbd_send_rep(QIOChannel *ioc, uint32_t type, uint32_t opt)
 {
     uint64_t magic;
     uint32_t len;
 
     magic = cpu_to_be64(NBD_REP_MAGIC);
-    if (write_sync(csock, &magic, sizeof(magic)) != sizeof(magic)) {
+    if (write_sync(ioc, &magic, sizeof(magic)) != sizeof(magic)) {
         LOG("write failed (rep magic)");
         return -EINVAL;
     }
     opt = cpu_to_be32(opt);
-    if (write_sync(csock, &opt, sizeof(opt)) != sizeof(opt)) {
+    if (write_sync(ioc, &opt, sizeof(opt)) != sizeof(opt)) {
         LOG("write failed (rep opt)");
         return -EINVAL;
     }
     type = cpu_to_be32(type);
-    if (write_sync(csock, &type, sizeof(type)) != sizeof(type)) {
+    if (write_sync(ioc, &type, sizeof(type)) != sizeof(type)) {
         LOG("write failed (rep type)");
         return -EINVAL;
     }
     len = cpu_to_be32(0);
-    if (write_sync(csock, &len, sizeof(len)) != sizeof(len)) {
+    if (write_sync(ioc, &len, sizeof(len)) != sizeof(len)) {
         LOG("write failed (rep data length)");
         return -EINVAL;
     }
     return 0;
 }
 
-static int nbd_send_rep_list(int csock, NBDExport *exp)
+static int nbd_send_rep_list(QIOChannel *ioc, NBDExport *exp)
 {
     uint64_t magic, name_len;
     uint32_t opt, type, len;
 
     name_len = strlen(exp->name);
     magic = cpu_to_be64(NBD_REP_MAGIC);
-    if (write_sync(csock, &magic, sizeof(magic)) != sizeof(magic)) {
+    if (write_sync(ioc, &magic, sizeof(magic)) != sizeof(magic)) {
         LOG("write failed (magic)");
         return -EINVAL;
      }
     opt = cpu_to_be32(NBD_OPT_LIST);
-    if (write_sync(csock, &opt, sizeof(opt)) != sizeof(opt)) {
+    if (write_sync(ioc, &opt, sizeof(opt)) != sizeof(opt)) {
         LOG("write failed (opt)");
         return -EINVAL;
     }
     type = cpu_to_be32(NBD_REP_SERVER);
-    if (write_sync(csock, &type, sizeof(type)) != sizeof(type)) {
+    if (write_sync(ioc, &type, sizeof(type)) != sizeof(type)) {
         LOG("write failed (reply type)");
         return -EINVAL;
     }
     len = cpu_to_be32(name_len + sizeof(len));
-    if (write_sync(csock, &len, sizeof(len)) != sizeof(len)) {
+    if (write_sync(ioc, &len, sizeof(len)) != sizeof(len)) {
         LOG("write failed (length)");
         return -EINVAL;
     }
     len = cpu_to_be32(name_len);
-    if (write_sync(csock, &len, sizeof(len)) != sizeof(len)) {
+    if (write_sync(ioc, &len, sizeof(len)) != sizeof(len)) {
         LOG("write failed (length)");
         return -EINVAL;
     }
-    if (write_sync(csock, exp->name, name_len) != name_len) {
+    if (write_sync(ioc, exp->name, name_len) != name_len) {
         LOG("write failed (buffer)");
         return -EINVAL;
     }
@@ -371,30 +428,31 @@ static int nbd_send_rep_list(int csock, NBDExport *exp)
 
 static int nbd_handle_list(NBDClient *client, uint32_t length)
 {
-    int csock;
+    QIOChannel *ioc;
     NBDExport *exp;
 
-    csock = client->sock;
+    ioc = client->ioc;
     if (length) {
-        if (drop_sync(csock, length) != length) {
+        if (drop_sync(ioc, length) != length) {
             return -EIO;
         }
-        return nbd_send_rep(csock, NBD_REP_ERR_INVALID, NBD_OPT_LIST);
+        return nbd_send_rep(ioc, NBD_REP_ERR_INVALID, NBD_OPT_LIST);
     }
 
     /* For each export, send a NBD_REP_SERVER reply. */
     QTAILQ_FOREACH(exp, &exports, next) {
-        if (nbd_send_rep_list(csock, exp)) {
+        if (nbd_send_rep_list(ioc, exp)) {
             return -EINVAL;
         }
     }
     /* Finish with a NBD_REP_ACK. */
-    return nbd_send_rep(csock, NBD_REP_ACK, NBD_OPT_LIST);
+    return nbd_send_rep(ioc, NBD_REP_ACK, NBD_OPT_LIST);
 }
 
 static int nbd_handle_export_name(NBDClient *client, uint32_t length)
 {
-    int rc = -EINVAL, csock = client->sock;
+    int rc = -EINVAL;
+    QIOChannel *ioc = client->ioc;
     char name[256];
 
     /* Client sends:
@@ -405,7 +463,7 @@ static int nbd_handle_export_name(NBDClient *client, uint32_t length)
         LOG("Bad length received");
         goto fail;
     }
-    if (read_sync(csock, name, length) != length) {
+    if (read_sync(ioc, name, length) != length) {
         LOG("read failed");
         goto fail;
     }
@@ -426,7 +484,7 @@ fail:
 
 static int nbd_receive_options(NBDClient *client)
 {
-    int csock = client->sock;
+    QIOChannel *ioc = client->ioc;
     uint32_t flags;
 
     /* Client sends:
@@ -443,7 +501,7 @@ static int nbd_receive_options(NBDClient *client)
         ...           Rest of request
     */
 
-    if (read_sync(csock, &flags, sizeof(flags)) != sizeof(flags)) {
+    if (read_sync(ioc, &flags, sizeof(flags)) != sizeof(flags)) {
         LOG("read failed");
         return -EIO;
     }
@@ -459,7 +517,7 @@ static int nbd_receive_options(NBDClient *client)
         uint32_t tmp, length;
         uint64_t magic;
 
-        if (read_sync(csock, &magic, sizeof(magic)) != sizeof(magic)) {
+        if (read_sync(ioc, &magic, sizeof(magic)) != sizeof(magic)) {
             LOG("read failed");
             return -EINVAL;
         }
@@ -469,12 +527,12 @@ static int nbd_receive_options(NBDClient *client)
             return -EINVAL;
         }
 
-        if (read_sync(csock, &tmp, sizeof(tmp)) != sizeof(tmp)) {
+        if (read_sync(ioc, &tmp, sizeof(tmp)) != sizeof(tmp)) {
             LOG("read failed");
             return -EINVAL;
         }
 
-        if (read_sync(csock, &length, sizeof(length)) != sizeof(length)) {
+        if (read_sync(ioc, &length, sizeof(length)) != sizeof(length)) {
             LOG("read failed");
             return -EINVAL;
         }
@@ -498,7 +556,7 @@ static int nbd_receive_options(NBDClient *client)
         default:
             tmp = be32_to_cpu(tmp);
             LOG("Unsupported option 0x%x", tmp);
-            nbd_send_rep(client->sock, NBD_REP_ERR_UNSUP, tmp);
+            nbd_send_rep(client->ioc, NBD_REP_ERR_UNSUP, tmp);
             return -EINVAL;
         }
     }
@@ -506,7 +564,7 @@ static int nbd_receive_options(NBDClient *client)
 
 static int nbd_send_negotiate(NBDClient *client)
 {
-    int csock = client->sock;
+    QIOChannel *ioc = client->ioc;
     char buf[8 + 8 + 8 + 128];
     int rc;
     const int myflags = (NBD_FLAG_HAS_FLAGS | NBD_FLAG_SEND_TRIM |
@@ -531,7 +589,7 @@ static int nbd_send_negotiate(NBDClient *client)
         [28 .. 151]   reserved     (0)
      */
 
-    qemu_set_block(csock);
+    qio_channel_set_blocking(ioc, true, NULL);
     rc = -EINVAL;
 
     TRACE("Beginning negotiation.");
@@ -548,12 +606,12 @@ static int nbd_send_negotiate(NBDClient *client)
     }
 
     if (client->exp) {
-        if (write_sync(csock, buf, sizeof(buf)) != sizeof(buf)) {
+        if (write_sync(ioc, buf, sizeof(buf)) != sizeof(buf)) {
             LOG("write failed");
             goto fail;
         }
     } else {
-        if (write_sync(csock, buf, 18) != 18) {
+        if (write_sync(ioc, buf, 18) != 18) {
             LOG("write failed");
             goto fail;
         }
@@ -566,7 +624,7 @@ static int nbd_send_negotiate(NBDClient *client)
         assert ((client->exp->nbdflags & ~65535) == 0);
         cpu_to_be64w((uint64_t*)(buf + 18), client->exp->size);
         cpu_to_be16w((uint16_t*)(buf + 26), client->exp->nbdflags | myflags);
-        if (write_sync(csock, buf + 18, sizeof(buf) - 18) != sizeof(buf) - 18) {
+        if (write_sync(ioc, buf + 18, sizeof(buf) - 18) != sizeof(buf) - 18) {
             LOG("write failed");
             goto fail;
         }
@@ -575,11 +633,11 @@ static int nbd_send_negotiate(NBDClient *client)
     TRACE("Negotiation succeeded.");
     rc = 0;
 fail:
-    qemu_set_nonblock(csock);
+    qio_channel_set_blocking(ioc, false, NULL);
     return rc;
 }
 
-int nbd_receive_negotiate(int csock, const char *name, uint32_t *flags,
+int nbd_receive_negotiate(QIOChannel *ioc, const char *name, uint32_t *flags,
                           off_t *size, Error **errp)
 {
     char buf[256];
@@ -591,7 +649,7 @@ int nbd_receive_negotiate(int csock, const char *name, uint32_t *flags,
 
     rc = -EINVAL;
 
-    if (read_sync(csock, buf, 8) != 8) {
+    if (read_sync(ioc, buf, 8) != 8) {
         error_setg(errp, "Failed to read data");
         goto fail;
     }
@@ -617,7 +675,7 @@ int nbd_receive_negotiate(int csock, const char *name, uint32_t *flags,
         goto fail;
     }
 
-    if (read_sync(csock, &magic, sizeof(magic)) != sizeof(magic)) {
+    if (read_sync(ioc, &magic, sizeof(magic)) != sizeof(magic)) {
         error_setg(errp, "Failed to read magic");
         goto fail;
     }
@@ -638,35 +696,35 @@ int nbd_receive_negotiate(int csock, const char *name, uint32_t *flags,
             }
             goto fail;
         }
-        if (read_sync(csock, &tmp, sizeof(tmp)) != sizeof(tmp)) {
+        if (read_sync(ioc, &tmp, sizeof(tmp)) != sizeof(tmp)) {
             error_setg(errp, "Failed to read server flags");
             goto fail;
         }
         *flags = be16_to_cpu(tmp) << 16;
         /* reserved for future use */
-        if (write_sync(csock, &reserved, sizeof(reserved)) !=
+        if (write_sync(ioc, &reserved, sizeof(reserved)) !=
             sizeof(reserved)) {
             error_setg(errp, "Failed to read reserved field");
             goto fail;
         }
         /* write the export name */
         magic = cpu_to_be64(magic);
-        if (write_sync(csock, &magic, sizeof(magic)) != sizeof(magic)) {
+        if (write_sync(ioc, &magic, sizeof(magic)) != sizeof(magic)) {
             error_setg(errp, "Failed to send export name magic");
             goto fail;
         }
         opt = cpu_to_be32(NBD_OPT_EXPORT_NAME);
-        if (write_sync(csock, &opt, sizeof(opt)) != sizeof(opt)) {
+        if (write_sync(ioc, &opt, sizeof(opt)) != sizeof(opt)) {
             error_setg(errp, "Failed to send export name option number");
             goto fail;
         }
         namesize = cpu_to_be32(strlen(name));
-        if (write_sync(csock, &namesize, sizeof(namesize)) !=
+        if (write_sync(ioc, &namesize, sizeof(namesize)) !=
             sizeof(namesize)) {
             error_setg(errp, "Failed to send export name length");
             goto fail;
         }
-        if (write_sync(csock, (char*)name, strlen(name)) != strlen(name)) {
+        if (write_sync(ioc, (char *)name, strlen(name)) != strlen(name)) {
             error_setg(errp, "Failed to send export name");
             goto fail;
         }
@@ -683,7 +741,7 @@ int nbd_receive_negotiate(int csock, const char *name, uint32_t *flags,
         }
     }
 
-    if (read_sync(csock, &s, sizeof(s)) != sizeof(s)) {
+    if (read_sync(ioc, &s, sizeof(s)) != sizeof(s)) {
         error_setg(errp, "Failed to read export length");
         goto fail;
     }
@@ -691,19 +749,19 @@ int nbd_receive_negotiate(int csock, const char *name, uint32_t *flags,
     TRACE("Size is %" PRIu64, *size);
 
     if (!name) {
-        if (read_sync(csock, flags, sizeof(*flags)) != sizeof(*flags)) {
+        if (read_sync(ioc, flags, sizeof(*flags)) != sizeof(*flags)) {
             error_setg(errp, "Failed to read export flags");
             goto fail;
         }
         *flags = be32_to_cpup(flags);
     } else {
-        if (read_sync(csock, &tmp, sizeof(tmp)) != sizeof(tmp)) {
+        if (read_sync(ioc, &tmp, sizeof(tmp)) != sizeof(tmp)) {
             error_setg(errp, "Failed to read export flags");
             goto fail;
         }
         *flags |= be16_to_cpu(tmp);
     }
-    if (read_sync(csock, &buf, 124) != 124) {
+    if (read_sync(ioc, &buf, 124) != 124) {
         error_setg(errp, "Failed to read reserved block");
         goto fail;
     }
@@ -714,11 +772,11 @@ fail:
 }
 
 #ifdef __linux__
-int nbd_init(int fd, int csock, uint32_t flags, off_t size)
+int nbd_init(int fd, QIOChannelSocket *sioc, uint32_t flags, off_t size)
 {
     TRACE("Setting NBD socket");
 
-    if (ioctl(fd, NBD_SET_SOCK, csock) < 0) {
+    if (ioctl(fd, NBD_SET_SOCK, sioc->fd) < 0) {
         int serrno = errno;
         LOG("Failed to set NBD socket");
         return -serrno;
@@ -799,7 +857,7 @@ int nbd_client(int fd)
     return ret;
 }
 #else
-int nbd_init(int fd, int csock, uint32_t flags, off_t size)
+int nbd_init(int fd, QIOChannel *ioc, uint32_t flags, off_t size)
 {
     return -ENOTSUP;
 }
@@ -815,7 +873,7 @@ int nbd_client(int fd)
 }
 #endif
 
-ssize_t nbd_send_request(int csock, struct nbd_request *request)
+ssize_t nbd_send_request(QIOChannel *ioc, struct nbd_request *request)
 {
     uint8_t buf[NBD_REQUEST_SIZE];
     ssize_t ret;
@@ -830,7 +888,7 @@ ssize_t nbd_send_request(int csock, struct nbd_request *request)
           "{ .from = %" PRIu64", .len = %u, .handle = %" PRIu64", .type=%i}",
           request->from, request->len, request->handle, request->type);
 
-    ret = write_sync(csock, buf, sizeof(buf));
+    ret = write_sync(ioc, buf, sizeof(buf));
     if (ret < 0) {
         return ret;
     }
@@ -842,13 +900,13 @@ ssize_t nbd_send_request(int csock, struct nbd_request *request)
     return 0;
 }
 
-static ssize_t nbd_receive_request(int csock, struct nbd_request *request)
+static ssize_t nbd_receive_request(QIOChannel *ioc, struct nbd_request *request)
 {
     uint8_t buf[NBD_REQUEST_SIZE];
     uint32_t magic;
     ssize_t ret;
 
-    ret = read_sync(csock, buf, sizeof(buf));
+    ret = read_sync(ioc, buf, sizeof(buf));
     if (ret < 0) {
         return ret;
     }
@@ -883,13 +941,13 @@ static ssize_t nbd_receive_request(int csock, struct nbd_request *request)
     return 0;
 }
 
-ssize_t nbd_receive_reply(int csock, struct nbd_reply *reply)
+ssize_t nbd_receive_reply(QIOChannel *ioc, struct nbd_reply *reply)
 {
     uint8_t buf[NBD_REPLY_SIZE];
     uint32_t magic;
     ssize_t ret;
 
-    ret = read_sync(csock, buf, sizeof(buf));
+    ret = read_sync(ioc, buf, sizeof(buf));
     if (ret < 0) {
         return ret;
     }
@@ -922,7 +980,7 @@ ssize_t nbd_receive_reply(int csock, struct nbd_reply *reply)
     return 0;
 }
 
-static ssize_t nbd_send_reply(int csock, struct nbd_reply *reply)
+static ssize_t nbd_send_reply(QIOChannel *ioc, struct nbd_reply *reply)
 {
     uint8_t buf[NBD_REPLY_SIZE];
     ssize_t ret;
@@ -940,7 +998,7 @@ static ssize_t nbd_send_reply(int csock, struct nbd_reply *reply)
 
     TRACE("Sending response to client");
 
-    ret = write_sync(csock, buf, sizeof(buf));
+    ret = write_sync(ioc, buf, sizeof(buf));
     if (ret < 0) {
         return ret;
     }
@@ -968,8 +1026,8 @@ void nbd_client_put(NBDClient *client)
         assert(client->closing);
 
         nbd_unset_handlers(client);
-        close(client->sock);
-        client->sock = -1;
+        object_unref(OBJECT(client->sioc));
+        object_unref(OBJECT(client->ioc));
         if (client->exp) {
             QTAILQ_REMOVE(&client->exp->clients, client, next);
             nbd_export_put(client->exp);
@@ -989,7 +1047,8 @@ static void client_close(NBDClient *client)
     /* Force requests to finish.  They will drop their own references,
      * then we'll close the socket and free the NBDClient.
      */
-    shutdown(client->sock, 2);
+    qio_channel_shutdown(client->ioc, QIO_CHANNEL_SHUTDOWN_BOTH,
+                         NULL);
 
     /* Also tell the client, so that they release their reference.  */
     if (client->close) {
@@ -1182,25 +1241,26 @@ static ssize_t nbd_co_send_reply(NBDRequest *req, struct nbd_reply *reply,
                                  int len)
 {
     NBDClient *client = req->client;
-    int csock = client->sock;
+    QIOChannel *ioc = client->ioc;
     ssize_t rc, ret;
 
+    g_assert(qemu_in_coroutine());
     qemu_co_mutex_lock(&client->send_lock);
     client->send_coroutine = qemu_coroutine_self();
     nbd_set_handlers(client);
 
     if (!len) {
-        rc = nbd_send_reply(csock, reply);
+        rc = nbd_send_reply(ioc, reply);
     } else {
-        socket_set_cork(csock, 1);
-        rc = nbd_send_reply(csock, reply);
+        qio_channel_set_cork(ioc, true);
+        rc = nbd_send_reply(ioc, reply);
         if (rc >= 0) {
-            ret = qemu_co_send(csock, req->data, len);
+            ret = write_sync(ioc, req->data, len);
             if (ret != len) {
                 rc = -EIO;
             }
         }
-        socket_set_cork(csock, 0);
+        qio_channel_set_cork(ioc, false);
     }
 
     client->send_coroutine = NULL;
@@ -1212,14 +1272,15 @@ static ssize_t nbd_co_send_reply(NBDRequest *req, struct nbd_reply *reply,
 static ssize_t nbd_co_receive_request(NBDRequest *req, struct nbd_request *request)
 {
     NBDClient *client = req->client;
-    int csock = client->sock;
+    QIOChannel *ioc = client->ioc;
     uint32_t command;
     ssize_t rc;
 
+    g_assert(qemu_in_coroutine());
     client->recv_coroutine = qemu_coroutine_self();
     nbd_update_can_read(client);
 
-    rc = nbd_receive_request(csock, request);
+    rc = nbd_receive_request(ioc, request);
     if (rc < 0) {
         if (rc != -EAGAIN) {
             rc = -EIO;
@@ -1250,7 +1311,7 @@ static ssize_t nbd_co_receive_request(NBDRequest *req, struct nbd_request *reque
     if (command == NBD_CMD_WRITE) {
         TRACE("Reading %u byte(s)", request->len);
 
-        if (qemu_co_recv(csock, req->data, request->len) != request->len) {
+        if (read_sync(ioc, req->data, request->len) != request->len) {
             LOG("reading from socket failed");
             rc = -EIO;
             goto out;
@@ -1445,7 +1506,7 @@ static void nbd_restart_write(void *opaque)
 static void nbd_set_handlers(NBDClient *client)
 {
     if (client->exp && client->exp->ctx) {
-        aio_set_fd_handler(client->exp->ctx, client->sock,
+        aio_set_fd_handler(client->exp->ctx, client->sioc->fd,
                            true,
                            client->can_read ? nbd_read : NULL,
                            client->send_coroutine ? nbd_restart_write : NULL,
@@ -1456,7 +1517,7 @@ static void nbd_set_handlers(NBDClient *client)
 static void nbd_unset_handlers(NBDClient *client)
 {
     if (client->exp && client->exp->ctx) {
-        aio_set_fd_handler(client->exp->ctx, client->sock,
+        aio_set_fd_handler(client->exp->ctx, client->sioc->fd,
                            true, NULL, NULL, NULL);
     }
 }
@@ -1475,16 +1536,22 @@ static void nbd_update_can_read(NBDClient *client)
     }
 }
 
-NBDClient *nbd_client_new(NBDExport *exp, int csock,
+NBDClient *nbd_client_new(NBDExport *exp,
+                          QIOChannelSocket *sioc,
                           void (*close)(NBDClient *))
 {
     NBDClient *client;
     client = g_malloc0(sizeof(NBDClient));
     client->refcount = 1;
     client->exp = exp;
-    client->sock = csock;
+    client->sioc = sioc;
+    object_ref(OBJECT(client->sioc));
+    client->ioc = QIO_CHANNEL(sioc);
+    object_ref(OBJECT(client->ioc));
     client->can_read = true;
     if (nbd_send_negotiate(client)) {
+        object_unref(OBJECT(client->sioc));
+        object_unref(OBJECT(client->ioc));
         g_free(client);
         return NULL;
     }
diff --git a/qemu-nbd.c b/qemu-nbd.c
index 28ab54f..42ccf05 100644
--- a/qemu-nbd.c
+++ b/qemu-nbd.c
@@ -252,7 +252,7 @@ static void *nbd_client_thread(void *arg)
         goto out;
     }
 
-    ret = nbd_receive_negotiate(sioc->fd, NULL, &nbdflags,
+    ret = nbd_receive_negotiate(QIO_CHANNEL(sioc), NULL, &nbdflags,
                                 &size, &local_error);
     if (ret < 0) {
         if (local_error) {
@@ -269,7 +269,7 @@ static void *nbd_client_thread(void *arg)
         goto out_socket;
     }
 
-    ret = nbd_init(fd, sioc->fd, nbdflags, size);
+    ret = nbd_init(fd, sioc, nbdflags, size);
     if (ret < 0) {
         goto out_fd;
     }
@@ -329,7 +329,6 @@ static void nbd_client_closed(NBDClient *client)
 static gboolean nbd_accept(QIOChannel *ioc, GIOCondition cond, gpointer opaque)
 {
     QIOChannelSocket *cioc;
-    int fd;
 
     cioc = qio_channel_socket_accept(QIO_CHANNEL_SOCKET(ioc),
                                      NULL);
@@ -342,9 +341,7 @@ static gboolean nbd_accept(QIOChannel *ioc, GIOCondition cond, gpointer opaque)
         return TRUE;
     }
 
-    fd = dup(cioc->fd);
-    if (fd >= 0 &&
-        nbd_client_new(exp, fd, nbd_client_closed)) {
+    if (nbd_client_new(exp, cioc, nbd_client_closed)) {
         nb_fds++;
         nbd_update_server_watch();
     }
-- 
2.5.0

^ permalink raw reply related	[flat|nested] 23+ messages in thread

* [Qemu-devel] [PATCH 07/15] nbd: invert client logic for negotiating protocol version
  2015-11-27 12:20 [Qemu-devel] [PATCH 00/15] Implement TLS support to QEMU NBD server & client Daniel P. Berrange
                   ` (5 preceding siblings ...)
  2015-11-27 12:20 ` [Qemu-devel] [PATCH 06/15] nbd: convert to using I/O channels for actual socket I/O Daniel P. Berrange
@ 2015-11-27 12:20 ` Daniel P. Berrange
  2015-11-27 12:20 ` [Qemu-devel] [PATCH 08/15] nbd: make server compliant with fixed newstyle spec Daniel P. Berrange
                   ` (9 subsequent siblings)
  16 siblings, 0 replies; 23+ messages in thread
From: Daniel P. Berrange @ 2015-11-27 12:20 UTC (permalink / raw)
  To: qemu-devel; +Cc: Paolo Bonzini, qemu-block, Wouter Verhelst

The nbd_receive_negotiate() method takes different code
paths based on whether 'name == NULL', and then checks
the expected protocol version in each branch.

This patch inverts the logic, so that it takes different
code paths based on what protocol version it receives and
then checks if name is NULL or not as needed.

This facilitates later code which allows the client to
be caapble of using the new style protocol regardless
of whether an export name is listed or not.

Signed-off-by: Daniel P. Berrange <berrange@redhat.com>
---
 nbd.c | 60 +++++++++++++++++++++++++++++-------------------------------
 1 file changed, 29 insertions(+), 31 deletions(-)

diff --git a/nbd.c b/nbd.c
index 701b951..bdfc45e 100644
--- a/nbd.c
+++ b/nbd.c
@@ -682,20 +682,11 @@ int nbd_receive_negotiate(QIOChannel *ioc, const char *name, uint32_t *flags,
     magic = be64_to_cpu(magic);
     TRACE("Magic is 0x%" PRIx64, magic);
 
-    if (name) {
+    if (magic == NBD_OPTS_MAGIC) {
         uint32_t reserved = 0;
         uint32_t opt;
         uint32_t namesize;
 
-        TRACE("Checking magic (opts_magic)");
-        if (magic != NBD_OPTS_MAGIC) {
-            if (magic == NBD_CLIENT_MAGIC) {
-                error_setg(errp, "Server does not support export names");
-            } else {
-                error_setg(errp, "Bad magic received");
-            }
-            goto fail;
-        }
         if (read_sync(ioc, &tmp, sizeof(tmp)) != sizeof(tmp)) {
             error_setg(errp, "Failed to read server flags");
             goto fail;
@@ -708,6 +699,10 @@ int nbd_receive_negotiate(QIOChannel *ioc, const char *name, uint32_t *flags,
             goto fail;
         }
         /* write the export name */
+        if (!name) {
+            error_setg(errp, "Server requires an export name");
+            goto fail;
+        }
         magic = cpu_to_be64(magic);
         if (write_sync(ioc, &magic, sizeof(magic)) != sizeof(magic)) {
             error_setg(errp, "Failed to send export name magic");
@@ -728,39 +723,42 @@ int nbd_receive_negotiate(QIOChannel *ioc, const char *name, uint32_t *flags,
             error_setg(errp, "Failed to send export name");
             goto fail;
         }
-    } else {
-        TRACE("Checking magic (cli_magic)");
 
-        if (magic != NBD_CLIENT_MAGIC) {
-            if (magic == NBD_OPTS_MAGIC) {
-                error_setg(errp, "Server requires an export name");
-            } else {
-                error_setg(errp, "Bad magic received");
-            }
+        if (read_sync(ioc, &s, sizeof(s)) != sizeof(s)) {
+            error_setg(errp, "Failed to read export length");
             goto fail;
         }
-    }
+        *size = be64_to_cpu(s);
+        TRACE("Size is %" PRIu64, *size);
 
-    if (read_sync(ioc, &s, sizeof(s)) != sizeof(s)) {
-        error_setg(errp, "Failed to read export length");
-        goto fail;
-    }
-    *size = be64_to_cpu(s);
-    TRACE("Size is %" PRIu64, *size);
+        if (read_sync(ioc, &tmp, sizeof(tmp)) != sizeof(tmp)) {
+            error_setg(errp, "Failed to read export flags");
+            goto fail;
+        }
+        *flags |= be16_to_cpu(tmp);
+    } else if (magic == NBD_CLIENT_MAGIC) {
+        if (name) {
+            error_setg(errp, "Server does not support export names");
+            goto fail;
+        }
+
+        if (read_sync(ioc, &s, sizeof(s)) != sizeof(s)) {
+            error_setg(errp, "Failed to read export length");
+            goto fail;
+        }
+        *size = be64_to_cpu(s);
+        TRACE("Size is %" PRIu64, *size);
 
-    if (!name) {
         if (read_sync(ioc, flags, sizeof(*flags)) != sizeof(*flags)) {
             error_setg(errp, "Failed to read export flags");
             goto fail;
         }
         *flags = be32_to_cpup(flags);
     } else {
-        if (read_sync(ioc, &tmp, sizeof(tmp)) != sizeof(tmp)) {
-            error_setg(errp, "Failed to read export flags");
-            goto fail;
-        }
-        *flags |= be16_to_cpu(tmp);
+        error_setg(errp, "Bad magic received");
+        goto fail;
     }
+
     if (read_sync(ioc, &buf, 124) != 124) {
         error_setg(errp, "Failed to read reserved block");
         goto fail;
-- 
2.5.0

^ permalink raw reply related	[flat|nested] 23+ messages in thread

* [Qemu-devel] [PATCH 08/15] nbd: make server compliant with fixed newstyle spec
  2015-11-27 12:20 [Qemu-devel] [PATCH 00/15] Implement TLS support to QEMU NBD server & client Daniel P. Berrange
                   ` (6 preceding siblings ...)
  2015-11-27 12:20 ` [Qemu-devel] [PATCH 07/15] nbd: invert client logic for negotiating protocol version Daniel P. Berrange
@ 2015-11-27 12:20 ` Daniel P. Berrange
  2015-11-27 12:20 ` [Qemu-devel] [PATCH 09/15] nbd: make client request fixed new style if advertized Daniel P. Berrange
                   ` (8 subsequent siblings)
  16 siblings, 0 replies; 23+ messages in thread
From: Daniel P. Berrange @ 2015-11-27 12:20 UTC (permalink / raw)
  To: qemu-devel; +Cc: Paolo Bonzini, qemu-block, Wouter Verhelst

If the client does not request the fixed new style protocol,
then we should only accept NBD_OPT_EXPORT_NAME. All other
options are only valid when fixed new style has been activated.

The qemu-nbd client doesn't currently request fixed new style
protocol, but this change won't break qemu-nbd, because it
fortunately only ever uses NBD_OPT_EXPORT_NAME, so was never
triggering the non-compliant server behaviour.

Signed-off-by: Daniel P. Berrange <berrange@redhat.com>
---
 nbd.c | 68 ++++++++++++++++++++++++++++++++++++++++++++-----------------------
 1 file changed, 45 insertions(+), 23 deletions(-)

diff --git a/nbd.c b/nbd.c
index bdfc45e..09a32a9 100644
--- a/nbd.c
+++ b/nbd.c
@@ -486,6 +486,7 @@ static int nbd_receive_options(NBDClient *client)
 {
     QIOChannel *ioc = client->ioc;
     uint32_t flags;
+    bool fixedNewstyle = false;
 
     /* Client sends:
         [ 0 ..   3]   client flags
@@ -507,14 +508,19 @@ static int nbd_receive_options(NBDClient *client)
     }
     TRACE("Checking client flags");
     be32_to_cpus(&flags);
-    if (flags != 0 && flags != NBD_FLAG_C_FIXED_NEWSTYLE) {
-        LOG("Bad client flags received");
+    if (flags & NBD_FLAG_C_FIXED_NEWSTYLE) {
+        TRACE("Support supports fixed newstyle handshake");
+        fixedNewstyle = true;
+        flags &= ~NBD_FLAG_C_FIXED_NEWSTYLE;
+    }
+    if (flags != 0) {
+        TRACE("Unknown client flags 0x%x received", flags);
         return -EIO;
     }
 
     while (1) {
         int ret;
-        uint32_t tmp, length;
+        uint32_t clientflags, length;
         uint64_t magic;
 
         if (read_sync(ioc, &magic, sizeof(magic)) != sizeof(magic)) {
@@ -527,10 +533,12 @@ static int nbd_receive_options(NBDClient *client)
             return -EINVAL;
         }
 
-        if (read_sync(ioc, &tmp, sizeof(tmp)) != sizeof(tmp)) {
+        if (read_sync(ioc, &clientflags, sizeof(clientflags)) !=
+            sizeof(clientflags)) {
             LOG("read failed");
             return -EINVAL;
         }
+        clientflags = be32_to_cpu(clientflags);
 
         if (read_sync(ioc, &length, sizeof(length)) != sizeof(length)) {
             LOG("read failed");
@@ -538,26 +546,40 @@ static int nbd_receive_options(NBDClient *client)
         }
         length = be32_to_cpu(length);
 
-        TRACE("Checking option");
-        switch (be32_to_cpu(tmp)) {
-        case NBD_OPT_LIST:
-            ret = nbd_handle_list(client, length);
-            if (ret < 0) {
-                return ret;
+        TRACE("Checking option 0x%x", clientflags);
+        if (fixedNewstyle) {
+            switch (clientflags) {
+            case NBD_OPT_LIST:
+                ret = nbd_handle_list(client, length);
+                if (ret < 0) {
+                    return ret;
+                }
+                break;
+
+            case NBD_OPT_ABORT:
+                return -EINVAL;
+
+            case NBD_OPT_EXPORT_NAME:
+                return nbd_handle_export_name(client, length);
+
+            default:
+                TRACE("Unsupported option 0x%x", clientflags);
+                nbd_send_rep(client->ioc, NBD_REP_ERR_UNSUP, clientflags);
+                return -EINVAL;
+            }
+        } else {
+            /*
+             * If broken new-style we should drop the connection
+             * for anything except NBD_OPT_EXPORT_NAME
+             */
+            switch (clientflags) {
+            case NBD_OPT_EXPORT_NAME:
+                return nbd_handle_export_name(client, length);
+
+            default:
+                TRACE("Unsupported option 0x%x", clientflags);
+                return -EINVAL;
             }
-            break;
-
-        case NBD_OPT_ABORT:
-            return -EINVAL;
-
-        case NBD_OPT_EXPORT_NAME:
-            return nbd_handle_export_name(client, length);
-
-        default:
-            tmp = be32_to_cpu(tmp);
-            LOG("Unsupported option 0x%x", tmp);
-            nbd_send_rep(client->ioc, NBD_REP_ERR_UNSUP, tmp);
-            return -EINVAL;
         }
     }
 }
-- 
2.5.0

^ permalink raw reply related	[flat|nested] 23+ messages in thread

* [Qemu-devel] [PATCH 09/15] nbd: make client request fixed new style if advertized
  2015-11-27 12:20 [Qemu-devel] [PATCH 00/15] Implement TLS support to QEMU NBD server & client Daniel P. Berrange
                   ` (7 preceding siblings ...)
  2015-11-27 12:20 ` [Qemu-devel] [PATCH 08/15] nbd: make server compliant with fixed newstyle spec Daniel P. Berrange
@ 2015-11-27 12:20 ` Daniel P. Berrange
  2015-11-27 12:20 ` [Qemu-devel] [PATCH 10/15] nbd: allow setting of an export name for qemu-nbd server Daniel P. Berrange
                   ` (7 subsequent siblings)
  16 siblings, 0 replies; 23+ messages in thread
From: Daniel P. Berrange @ 2015-11-27 12:20 UTC (permalink / raw)
  To: qemu-devel; +Cc: Paolo Bonzini, qemu-block, Wouter Verhelst

If the server advertizes support for the fixed new style
negotiation, the client should in turn enable new style.
This will allow the client to negotiate further NBD
options besides the export name.

Signed-off-by: Daniel P. Berrange <berrange@redhat.com>
---
 nbd.c | 27 +++++++++++++++++----------
 1 file changed, 17 insertions(+), 10 deletions(-)

diff --git a/nbd.c b/nbd.c
index 09a32a9..6ec04a7 100644
--- a/nbd.c
+++ b/nbd.c
@@ -664,7 +664,6 @@ int nbd_receive_negotiate(QIOChannel *ioc, const char *name, uint32_t *flags,
 {
     char buf[256];
     uint64_t magic, s;
-    uint16_t tmp;
     int rc;
 
     TRACE("Receiving negotiation.");
@@ -705,19 +704,26 @@ int nbd_receive_negotiate(QIOChannel *ioc, const char *name, uint32_t *flags,
     TRACE("Magic is 0x%" PRIx64, magic);
 
     if (magic == NBD_OPTS_MAGIC) {
-        uint32_t reserved = 0;
+        uint32_t clientflags = 0;
         uint32_t opt;
         uint32_t namesize;
+        uint16_t globalflags;
+        uint16_t exportflags;
 
-        if (read_sync(ioc, &tmp, sizeof(tmp)) != sizeof(tmp)) {
+        if (read_sync(ioc, &globalflags, sizeof(globalflags)) !=
+            sizeof(globalflags)) {
             error_setg(errp, "Failed to read server flags");
             goto fail;
         }
-        *flags = be16_to_cpu(tmp) << 16;
-        /* reserved for future use */
-        if (write_sync(ioc, &reserved, sizeof(reserved)) !=
-            sizeof(reserved)) {
-            error_setg(errp, "Failed to read reserved field");
+        *flags = be16_to_cpu(globalflags) << 16;
+        if (globalflags & NBD_FLAG_FIXED_NEWSTYLE) {
+            TRACE("Server supports fixed new style");
+            clientflags |= NBD_FLAG_C_FIXED_NEWSTYLE;
+        }
+        /* client requested flags */
+        if (write_sync(ioc, &clientflags, sizeof(clientflags)) !=
+            sizeof(clientflags)) {
+            error_setg(errp, "Failed to send clientflags field");
             goto fail;
         }
         /* write the export name */
@@ -753,11 +759,12 @@ int nbd_receive_negotiate(QIOChannel *ioc, const char *name, uint32_t *flags,
         *size = be64_to_cpu(s);
         TRACE("Size is %" PRIu64, *size);
 
-        if (read_sync(ioc, &tmp, sizeof(tmp)) != sizeof(tmp)) {
+        if (read_sync(ioc, &exportflags, sizeof(exportflags)) !=
+            sizeof(exportflags)) {
             error_setg(errp, "Failed to read export flags");
             goto fail;
         }
-        *flags |= be16_to_cpu(tmp);
+        *flags |= be16_to_cpu(exportflags);
     } else if (magic == NBD_CLIENT_MAGIC) {
         if (name) {
             error_setg(errp, "Server does not support export names");
-- 
2.5.0

^ permalink raw reply related	[flat|nested] 23+ messages in thread

* [Qemu-devel] [PATCH 10/15] nbd: allow setting of an export name for qemu-nbd server
  2015-11-27 12:20 [Qemu-devel] [PATCH 00/15] Implement TLS support to QEMU NBD server & client Daniel P. Berrange
                   ` (8 preceding siblings ...)
  2015-11-27 12:20 ` [Qemu-devel] [PATCH 09/15] nbd: make client request fixed new style if advertized Daniel P. Berrange
@ 2015-11-27 12:20 ` Daniel P. Berrange
  2015-11-27 12:20 ` [Qemu-devel] [PATCH 11/15] nbd: pick first exported volume if no export name is requested Daniel P. Berrange
                   ` (6 subsequent siblings)
  16 siblings, 0 replies; 23+ messages in thread
From: Daniel P. Berrange @ 2015-11-27 12:20 UTC (permalink / raw)
  To: qemu-devel; +Cc: Paolo Bonzini, qemu-block, Wouter Verhelst

The qemu-nbd server currently always uses the old style protocol
since it never sets any export name. This is a problem because
future TLS support will require use of the new style protocol
negotiation.

This adds a "--exportname NAME" argument to qemu-nbd which allows
the user to set an explicit export name. When --exportname is
set the server will always use the new style protocol.

Signed-off-by: Daniel P. Berrange <berrange@redhat.com>
---
 qemu-nbd.c    | 14 ++++++++++++--
 qemu-nbd.texi |  3 +++
 2 files changed, 15 insertions(+), 2 deletions(-)

diff --git a/qemu-nbd.c b/qemu-nbd.c
index 42ccf05..3f7f2222 100644
--- a/qemu-nbd.c
+++ b/qemu-nbd.c
@@ -47,6 +47,7 @@
 #define QEMU_NBD_OPT_OBJECT        5
 
 static NBDExport *exp;
+static bool newproto;
 static int verbose;
 static char *srcpath;
 static SocketAddress *saddr;
@@ -341,7 +342,7 @@ static gboolean nbd_accept(QIOChannel *ioc, GIOCondition cond, gpointer opaque)
         return TRUE;
     }
 
-    if (nbd_client_new(exp, cioc, nbd_client_closed)) {
+    if (nbd_client_new(newproto ? NULL : exp, cioc, nbd_client_closed)) {
         nb_fds++;
         nbd_update_server_watch();
     }
@@ -469,7 +470,7 @@ int main(int argc, char **argv)
     off_t fd_size;
     QemuOpts *sn_opts = NULL;
     const char *sn_id_or_name = NULL;
-    const char *sopt = "hVb:o:p:rsnP:c:dvk:e:f:tl:";
+    const char *sopt = "hVb:o:p:rsnP:c:dvk:e:f:tl:x:";
     struct option lopt[] = {
         { "help", 0, NULL, 'h' },
         { "version", 0, NULL, 'V' },
@@ -493,6 +494,7 @@ int main(int argc, char **argv)
         { "persistent", 0, NULL, 't' },
         { "verbose", 0, NULL, 'v' },
         { "object", 1, NULL, QEMU_NBD_OPT_OBJECT },
+        { "exportname", 1, NULL, 'x' },
         { NULL, 0, NULL, 0 }
     };
     int ch;
@@ -510,6 +512,7 @@ int main(int argc, char **argv)
     BlockdevDetectZeroesOptions detect_zeroes = BLOCKDEV_DETECT_ZEROES_OPTIONS_OFF;
     QDict *options = NULL;
     QemuOpts *opts;
+    const char *exportname = NULL;
 
     /* The client thread uses SIGTERM to interrupt the server.  A signal
      * handler ensures that "qemu-nbd -v -c" exits with a nice status code.
@@ -645,6 +648,9 @@ int main(int argc, char **argv)
         case 't':
             persistent = 1;
             break;
+        case 'x':
+            exportname = optarg;
+            break;
         case 'v':
             verbose = 1;
             break;
@@ -812,6 +818,10 @@ int main(int argc, char **argv)
     if (!exp) {
         errx(EXIT_FAILURE, "%s", error_get_pretty(local_err));
     }
+    if (exportname) {
+        nbd_export_set_name(exp, exportname);
+        newproto = true;
+    }
 
     server_ioc = qio_channel_socket_new();
     if (qio_channel_socket_listen_sync(server_ioc, saddr, &local_err) < 0) {
diff --git a/qemu-nbd.texi b/qemu-nbd.texi
index 3b2004e..6ff3920 100644
--- a/qemu-nbd.texi
+++ b/qemu-nbd.texi
@@ -64,6 +64,9 @@ Export QEMU disk image using NBD protocol.
   force block driver for format @var{fmt} instead of auto-detecting
 @item -t, --persistent
   don't exit on the last connection
+@item -x NAME, --exportname=NAME
+  set the NDB volume export name. This switches the server to use
+  the new style NBD protocol negotiation
 @item -v, --verbose
   display extra debugging information
 @item -h, --help
-- 
2.5.0

^ permalink raw reply related	[flat|nested] 23+ messages in thread

* [Qemu-devel] [PATCH 11/15] nbd: pick first exported volume if no export name is requested
  2015-11-27 12:20 [Qemu-devel] [PATCH 00/15] Implement TLS support to QEMU NBD server & client Daniel P. Berrange
                   ` (9 preceding siblings ...)
  2015-11-27 12:20 ` [Qemu-devel] [PATCH 10/15] nbd: allow setting of an export name for qemu-nbd server Daniel P. Berrange
@ 2015-11-27 12:20 ` Daniel P. Berrange
  2015-11-27 12:20 ` [Qemu-devel] [PATCH 12/15] nbd: implement TLS support in the protocol negotiation Daniel P. Berrange
                   ` (5 subsequent siblings)
  16 siblings, 0 replies; 23+ messages in thread
From: Daniel P. Berrange @ 2015-11-27 12:20 UTC (permalink / raw)
  To: qemu-devel; +Cc: Paolo Bonzini, qemu-block, Wouter Verhelst

The NBD client is currently only capable of using the new style
protocol negotiation if an explicit export name is provided.
This is a problem, because TLS support will require use of the
new style protocol in all cases, and we wish to keep the export
name as an optional request for backwards compatibility.

The trivial way to deal with this is to use the NBD protocol
option for listing exports and then pick the first returned
export as the one to use. This makes the client "do the right
thing" in the normal case where the qemu-nbd server only
exports a single volume.

Furthermore, in order to get proper error reporting of unknown
options with fixed new style protocol, we must not send the
NBD_OPT_EXPORT_NAME as the first thing. Thus, even if an export
name is provided we still send NBD_OPT_LIST to enumerate server
exports. This also gives us clearer error reporting in the case
that the requested export name does not exist. If NBD_OPT_LIST
is not supported, we still fallback to using the specified
export name, so as not to break compatibility with old QEMU
NBD server impls that predated NBD_OPT_LIST

Signed-off-by: Daniel P. Berrange <berrange@redhat.com>
---
 nbd.c | 202 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++--
 1 file changed, 197 insertions(+), 5 deletions(-)

diff --git a/nbd.c b/nbd.c
index 6ec04a7..0f6c755 100644
--- a/nbd.c
+++ b/nbd.c
@@ -468,6 +468,7 @@ static int nbd_handle_export_name(NBDClient *client, uint32_t length)
         goto fail;
     }
     name[length] = '\0';
+    TRACE("Client requested export '%s'", name);
 
     client->exp = nbd_export_find(name);
     if (!client->exp) {
@@ -659,12 +660,187 @@ fail:
     return rc;
 }
 
+
+static int nbd_handle_reply_err(uint32_t opt, uint32_t type, Error **errp)
+{
+    if (!(type & (1 << 31))) {
+        return 0;
+    }
+
+    switch (type) {
+    case NBD_REP_ERR_UNSUP:
+        error_setg(errp, "Unsupported option type %x", opt);
+        break;
+
+    case NBD_REP_ERR_INVALID:
+        error_setg(errp, "Invalid data length for option %x", opt);
+        break;
+
+    default:
+        error_setg(errp, "Unknown error code when asking for option %x", opt);
+        break;
+    }
+
+    return -1;
+}
+
+static int nbd_receive_list(QIOChannel *ioc, char **name, Error **errp)
+{
+    uint64_t magic;
+    uint32_t opt;
+    uint32_t type;
+    uint32_t len;
+    uint32_t namelen;
+
+    *name = NULL;
+    if (read_sync(ioc, &magic, sizeof(magic)) != sizeof(magic)) {
+        error_setg(errp, "failed to read list option magic");
+        return -1;
+    }
+    magic = be64_to_cpu(magic);
+    if (magic != NBD_REP_MAGIC) {
+        error_setg(errp, "Unexpected option list magic");
+        return -1;
+    }
+    if (read_sync(ioc, &opt, sizeof(opt)) != sizeof(opt)) {
+        error_setg(errp, "failed to read list option");
+        return -1;
+    }
+    opt = be32_to_cpu(opt);
+    if (opt != NBD_OPT_LIST) {
+        error_setg(errp, "Unexpected option type %x expected %x",
+                   opt, NBD_OPT_LIST);
+        return -1;
+    }
+
+    if (read_sync(ioc, &type, sizeof(type)) != sizeof(type)) {
+        error_setg(errp, "failed to read list option type");
+        return -1;
+    }
+    type = be32_to_cpu(type);
+    if (type == NBD_REP_ERR_UNSUP) {
+        return 0;
+    }
+    if (nbd_handle_reply_err(opt, type, errp) < 0) {
+        return -1;
+    }
+
+    if (read_sync(ioc, &len, sizeof(len)) != sizeof(len)) {
+        error_setg(errp, "failed to read option length");
+        return -1;
+    }
+    len = be32_to_cpu(len);
+
+    if (type == NBD_REP_ACK) {
+        if (len != 0) {
+            error_setg(errp, "length too long for option end");
+            return -1;
+        }
+    } else if (type == NBD_REP_SERVER) {
+        if (read_sync(ioc, &namelen, sizeof(namelen)) != sizeof(namelen)) {
+            error_setg(errp, "failed to read option name length");
+            return -1;
+        }
+        namelen = be32_to_cpu(namelen);
+        if (len != (namelen + sizeof(namelen))) {
+            error_setg(errp, "incorrect option mame length");
+            return -1;
+        }
+        if (namelen > 255) {
+            error_setg(errp, "export name length too long %d", namelen);
+            return -1;
+        }
+
+        *name = g_new0(char, namelen + 1);
+        if (read_sync(ioc, *name, namelen) != namelen) {
+            error_setg(errp, "failed to read export name");
+            g_free(*name);
+            *name = NULL;
+            return -1;
+        }
+        (*name)[namelen] = '\0';
+    } else {
+        error_setg(errp, "Unexpected reply type %x expected %x",
+                   type, NBD_REP_SERVER);
+        return -1;
+    }
+    return 1;
+}
+
+
+static char *nbd_receive_pick_export(QIOChannel *ioc,
+                                     const char *wantname,
+                                     Error **errp)
+{
+    uint64_t magic = cpu_to_be64(NBD_OPTS_MAGIC);
+    uint32_t opt = cpu_to_be32(NBD_OPT_LIST);
+    uint32_t length = 0;
+    char *chosenname = NULL;
+
+    TRACE("Picking export name");
+    if (write_sync(ioc, &magic, sizeof(magic)) != sizeof(magic)) {
+        error_setg(errp, "Failed to send list option magic");
+        goto fail;
+    }
+
+    if (write_sync(ioc, &opt, sizeof(opt)) != sizeof(opt)) {
+        error_setg(errp, "Failed to send list option number");
+        goto fail;
+    }
+
+    if (write_sync(ioc, &length, sizeof(length)) != sizeof(length)) {
+        error_setg(errp, "Failed to send list option length");
+        goto fail;
+    }
+
+    TRACE("Reading available export names");
+    while (1) {
+        char *name = NULL;
+        int ret = nbd_receive_list(ioc, &name, errp);
+
+        if (ret < 0) {
+            g_free(name);
+            name = NULL;
+            goto fail;
+        }
+        if (ret == 0) { /* Server doesn't support export listing */
+            if (wantname == NULL) {
+                error_setg(errp, "Export name is required");
+                goto fail;
+            }
+            chosenname = g_strdup(wantname);
+            break;
+        }
+        if (name == NULL) {
+            TRACE("End of export name list");
+            break;
+        }
+        if (!chosenname && (!wantname || g_str_equal(name, wantname))) {
+            chosenname = name;
+            TRACE("Picked export name %s", name);
+        } else {
+            TRACE("Ignored export name %s", name);
+            g_free(name);
+        }
+    }
+
+    if (wantname && !chosenname) {
+        error_setg(errp, "No export with name %s available", wantname);
+        goto fail;
+    }
+
+ fail:
+    TRACE("Chosen export name %s", chosenname ? chosenname : "<null>");
+    return chosenname;
+}
+
 int nbd_receive_negotiate(QIOChannel *ioc, const char *name, uint32_t *flags,
                           off_t *size, Error **errp)
 {
     char buf[256];
     uint64_t magic, s;
     int rc;
+    char *autoname = NULL;
 
     TRACE("Receiving negotiation.");
 
@@ -709,27 +885,40 @@ int nbd_receive_negotiate(QIOChannel *ioc, const char *name, uint32_t *flags,
         uint32_t namesize;
         uint16_t globalflags;
         uint16_t exportflags;
+        bool fixedNewStyle = false;
 
         if (read_sync(ioc, &globalflags, sizeof(globalflags)) !=
             sizeof(globalflags)) {
             error_setg(errp, "Failed to read server flags");
             goto fail;
         }
-        *flags = be16_to_cpu(globalflags) << 16;
+        globalflags = be16_to_cpu(globalflags);
+        *flags = globalflags << 16;
+        TRACE("Global flags are %x", globalflags);
         if (globalflags & NBD_FLAG_FIXED_NEWSTYLE) {
+            fixedNewStyle = true;
             TRACE("Server supports fixed new style");
             clientflags |= NBD_FLAG_C_FIXED_NEWSTYLE;
         }
         /* client requested flags */
+        clientflags = cpu_to_be32(clientflags);
         if (write_sync(ioc, &clientflags, sizeof(clientflags)) !=
             sizeof(clientflags)) {
             error_setg(errp, "Failed to send clientflags field");
             goto fail;
         }
         /* write the export name */
-        if (!name) {
-            error_setg(errp, "Server requires an export name");
-            goto fail;
+        if (fixedNewStyle) {
+            autoname = nbd_receive_pick_export(ioc, name, errp);
+            if (!autoname) {
+                goto fail;
+            }
+            name = autoname;
+        } else {
+            if (!name) {
+                error_setg(errp, "Server requires an export name");
+                goto fail;
+            }
         }
         magic = cpu_to_be64(magic);
         if (write_sync(ioc, &magic, sizeof(magic)) != sizeof(magic)) {
@@ -764,7 +953,9 @@ int nbd_receive_negotiate(QIOChannel *ioc, const char *name, uint32_t *flags,
             error_setg(errp, "Failed to read export flags");
             goto fail;
         }
-        *flags |= be16_to_cpu(exportflags);
+        exportflags = be16_to_cpu(exportflags);
+        *flags |= exportflags;
+        TRACE("Export flags are %x", exportflags);
     } else if (magic == NBD_CLIENT_MAGIC) {
         if (name) {
             error_setg(errp, "Server does not support export names");
@@ -795,6 +986,7 @@ int nbd_receive_negotiate(QIOChannel *ioc, const char *name, uint32_t *flags,
     rc = 0;
 
 fail:
+    free(autoname);
     return rc;
 }
 
-- 
2.5.0

^ permalink raw reply related	[flat|nested] 23+ messages in thread

* [Qemu-devel] [PATCH 12/15] nbd: implement TLS support in the protocol negotiation
  2015-11-27 12:20 [Qemu-devel] [PATCH 00/15] Implement TLS support to QEMU NBD server & client Daniel P. Berrange
                   ` (10 preceding siblings ...)
  2015-11-27 12:20 ` [Qemu-devel] [PATCH 11/15] nbd: pick first exported volume if no export name is requested Daniel P. Berrange
@ 2015-11-27 12:20 ` Daniel P. Berrange
  2015-11-28 10:28   ` Wouter Verhelst
  2015-11-27 12:20 ` [Qemu-devel] [PATCH 13/15] nbd: enable use of TLS with NBD block driver Daniel P. Berrange
                   ` (4 subsequent siblings)
  16 siblings, 1 reply; 23+ messages in thread
From: Daniel P. Berrange @ 2015-11-27 12:20 UTC (permalink / raw)
  To: qemu-devel; +Cc: Paolo Bonzini, qemu-block, Wouter Verhelst

This extends the NBD protocol handling code so that it is capable
of negotiating TLS support during the connection setup. This involves
requesting the STARTTLS protocol option before any other NBD options.

Signed-off-by: Daniel P. Berrange <berrange@redhat.com>
---
 block/nbd-client.c  |  12 ++-
 blockdev-nbd.c      |   2 +-
 include/block/nbd.h |   7 ++
 nbd.c               | 284 +++++++++++++++++++++++++++++++++++++++++++++++++---
 qemu-nbd.c          |   4 +-
 5 files changed, 290 insertions(+), 19 deletions(-)

diff --git a/block/nbd-client.c b/block/nbd-client.c
index ed69d4b..f7bacfd 100644
--- a/block/nbd-client.c
+++ b/block/nbd-client.c
@@ -389,7 +389,10 @@ int nbd_client_init(BlockDriverState *bs, QIOChannelSocket *sioc,
     qio_channel_set_blocking(QIO_CHANNEL(sioc), true, NULL);
 
     ret = nbd_receive_negotiate(QIO_CHANNEL(sioc), export,
-                                &client->nbdflags, &client->size, errp);
+                                &client->nbdflags,
+                                NULL, NULL,
+                                &client->ioc,
+                                &client->size, errp);
     if (ret < 0) {
         logout("Failed to negotiate with the NBD server\n");
         return ret;
@@ -399,8 +402,11 @@ int nbd_client_init(BlockDriverState *bs, QIOChannelSocket *sioc,
     qemu_co_mutex_init(&client->free_sema);
     client->sioc = sioc;
     object_ref(OBJECT(client->sioc));
-    client->ioc = QIO_CHANNEL(sioc);
-    object_ref(OBJECT(client->ioc));
+
+    if (!client->ioc) {
+        client->ioc = QIO_CHANNEL(sioc);
+        object_ref(OBJECT(client->ioc));
+    }
 
     /* Now that we're connected, set the socket to be non-blocking and
      * kick the reply mechanism.  */
diff --git a/blockdev-nbd.c b/blockdev-nbd.c
index 7d4c415..6af1684 100644
--- a/blockdev-nbd.c
+++ b/blockdev-nbd.c
@@ -33,7 +33,7 @@ static gboolean nbd_accept(QIOChannel *ioc, GIOCondition condition,
         return TRUE;
     }
 
-    nbd_client_new(NULL, cioc, nbd_client_put);
+    nbd_client_new(NULL, cioc, NULL, NULL, nbd_client_put);
     object_unref(OBJECT(cioc));
     return TRUE;
 }
diff --git a/include/block/nbd.h b/include/block/nbd.h
index 0230c7f..502edfe 100644
--- a/include/block/nbd.h
+++ b/include/block/nbd.h
@@ -24,6 +24,7 @@
 #include "qemu-common.h"
 #include "qemu/option.h"
 #include "io/channel-socket.h"
+#include "crypto/tlscreds.h"
 
 struct nbd_request {
     uint32_t magic;
@@ -57,6 +58,8 @@ struct nbd_reply {
 #define NBD_REP_SERVER          (2)             /* Export description. */
 #define NBD_REP_ERR_UNSUP       ((UINT32_C(1) << 31) | 1) /* Unknown option. */
 #define NBD_REP_ERR_INVALID     ((UINT32_C(1) << 31) | 3) /* Invalid length. */
+#define NBD_REP_ERR_TLS_REQD    ((UINT32_C(1) << 31) | 5) /* TLS required */
+
 
 #define NBD_CMD_MASK_COMMAND	0x0000ffff
 #define NBD_CMD_FLAG_FUA	(1 << 16)
@@ -81,6 +84,8 @@ ssize_t nbd_wr_syncv(QIOChannel *ioc,
                      size_t length,
                      bool do_read);
 int nbd_receive_negotiate(QIOChannel *ioc, const char *name, uint32_t *flags,
+                          QCryptoTLSCreds *tlscreds, const char *hostname,
+                          QIOChannel **outioc,
                           off_t *size, Error **errp);
 int nbd_init(int fd, QIOChannelSocket *sioc, uint32_t flags, off_t size);
 ssize_t nbd_send_request(QIOChannel *ioc, struct nbd_request *request);
@@ -106,6 +111,8 @@ void nbd_export_close_all(void);
 
 NBDClient *nbd_client_new(NBDExport *exp,
                           QIOChannelSocket *sioc,
+                          QCryptoTLSCreds *tlscreds,
+                          const char *tlsaclname,
                           void (*close)(NBDClient *));
 void nbd_client_get(NBDClient *client);
 void nbd_client_put(NBDClient *client);
diff --git a/nbd.c b/nbd.c
index 0f6c755..f17500e 100644
--- a/nbd.c
+++ b/nbd.c
@@ -18,6 +18,7 @@
 
 #include "block/nbd.h"
 #include "sysemu/block-backend.h"
+#include "io/channel-tls.h"
 
 #include "qemu/coroutine.h"
 #include "qemu/iov.h"
@@ -85,6 +86,8 @@
 #define NBD_OPT_EXPORT_NAME     (1)
 #define NBD_OPT_ABORT           (2)
 #define NBD_OPT_LIST            (3)
+#define NBD_OPT_PEEK_EXPORT     (4)
+#define NBD_OPT_STARTTLS        (5)
 
 /* NBD errors are based on errno numbers, so there is a 1:1 mapping,
  * but only a limited set of errno values is specified in the protocol.
@@ -171,6 +174,8 @@ struct NBDClient {
     void (*close)(NBDClient *client);
 
     NBDExport *exp;
+    QCryptoTLSCreds *tlscreds;
+    char *tlsaclname;
     QIOChannelSocket *sioc; /* The underlying data channel */
     QIOChannel *ioc; /* The current I/O channel which may differ (eg TLS) */
 
@@ -365,6 +370,8 @@ static int nbd_send_rep(QIOChannel *ioc, uint32_t type, uint32_t opt)
     uint64_t magic;
     uint32_t len;
 
+    TRACE("Reply opt=%x type=%x", type, opt);
+
     magic = cpu_to_be64(NBD_REP_MAGIC);
     if (write_sync(ioc, &magic, sizeof(magic)) != sizeof(magic)) {
         LOG("write failed (rep magic)");
@@ -483,6 +490,74 @@ fail:
     return rc;
 }
 
+
+struct NBDTLSHandshakeData {
+    GMainLoop *loop;
+    bool complete;
+    Error *error;
+};
+
+static void nbd_tls_handshake(Object *src,
+                              Error *err,
+                              void *opaque)
+{
+    struct NBDTLSHandshakeData *data = opaque;
+
+    if (err) {
+        TRACE("TLS failed %s", error_get_pretty(err));
+        data->error = error_copy(err);
+    }
+    data->complete = true;
+    g_main_loop_quit(data->loop);
+}
+
+static QIOChannel *nbd_handle_starttls(NBDClient *client, uint32_t length)
+{
+    QIOChannel *ioc;
+    QIOChannelTLS *tioc;
+    struct NBDTLSHandshakeData data = { 0 };
+
+    TRACE("Setting up TLS");
+    ioc = client->ioc;
+    if (length) {
+        if (drop_sync(ioc, length) != length) {
+            return NULL;
+        }
+        nbd_send_rep(ioc, NBD_REP_ERR_INVALID, NBD_OPT_STARTTLS);
+        return NULL;
+    }
+
+    nbd_send_rep(client->ioc, NBD_REP_ACK, NBD_OPT_STARTTLS);
+
+    tioc = qio_channel_tls_new_server(ioc,
+                                      client->tlscreds,
+                                      client->tlsaclname,
+                                      NULL);
+    if (!tioc) {
+        return NULL;
+    }
+
+    TRACE("Starting TLS handshake");
+    data.loop = g_main_loop_new(g_main_context_default(), FALSE);
+    qio_channel_tls_handshake(tioc,
+                              nbd_tls_handshake,
+                              &data,
+                              NULL);
+
+    if (!data.complete) {
+        g_main_loop_run(data.loop);
+    }
+    g_main_loop_unref(data.loop);
+    if (data.error) {
+        object_unref(OBJECT(tioc));
+        error_free(data.error);
+        return NULL;
+    }
+
+    return QIO_CHANNEL(tioc);
+}
+
+
 static int nbd_receive_options(NBDClient *client)
 {
     QIOChannel *ioc = client->ioc;
@@ -548,7 +623,28 @@ static int nbd_receive_options(NBDClient *client)
         length = be32_to_cpu(length);
 
         TRACE("Checking option 0x%x", clientflags);
-        if (fixedNewstyle) {
+        if (client->tlscreds &&
+            ioc == (QIOChannel *)client->sioc) {
+            if (!fixedNewstyle) {
+                TRACE("Unsupported option 0x%x", clientflags);
+                return -EINVAL;
+            }
+            switch (clientflags) {
+            case NBD_OPT_STARTTLS:
+                ioc = nbd_handle_starttls(client, length);
+                if (!ioc) {
+                    return -EIO;
+                }
+                object_unref(OBJECT(client->ioc));
+                client->ioc = QIO_CHANNEL(ioc);
+                break;
+
+            default:
+                TRACE("Option 0x%x not permitted before TLS", clientflags);
+                nbd_send_rep(client->ioc, NBD_REP_ERR_TLS_REQD, clientflags);
+                return -EINVAL;
+            }
+        } else if (fixedNewstyle) {
             switch (clientflags) {
             case NBD_OPT_LIST:
                 ret = nbd_handle_list(client, length);
@@ -563,6 +659,14 @@ static int nbd_receive_options(NBDClient *client)
             case NBD_OPT_EXPORT_NAME:
                 return nbd_handle_export_name(client, length);
 
+            case NBD_OPT_STARTTLS:
+                if (client->tlscreds) {
+                    TRACE("TLS already enabled");
+                } else {
+                    TRACE("TLS not configured");
+                }
+                nbd_send_rep(client->ioc, NBD_REP_ERR_UNSUP, clientflags);
+                return -EINVAL;
             default:
                 TRACE("Unsupported option 0x%x", clientflags);
                 nbd_send_rep(client->ioc, NBD_REP_ERR_UNSUP, clientflags);
@@ -587,13 +691,13 @@ static int nbd_receive_options(NBDClient *client)
 
 static int nbd_send_negotiate(NBDClient *client)
 {
-    QIOChannel *ioc = client->ioc;
     char buf[8 + 8 + 8 + 128];
     int rc;
     const int myflags = (NBD_FLAG_HAS_FLAGS | NBD_FLAG_SEND_TRIM |
                          NBD_FLAG_SEND_FLUSH | NBD_FLAG_SEND_FUA);
+    bool oldStyle;
 
-    /* Negotiation header without options:
+    /* Old style negotiation header without options:
         [ 0 ..   7]   passwd       ("NBDMAGIC")
         [ 8 ..  15]   magic        (NBD_CLIENT_MAGIC)
         [16 ..  23]   size
@@ -601,24 +705,25 @@ static int nbd_send_negotiate(NBDClient *client)
         [26 ..  27]   export flags
         [28 .. 151]   reserved     (0)
 
-       Negotiation header with options, part 1:
+       New style negotiation header with options:
         [ 0 ..   7]   passwd       ("NBDMAGIC")
         [ 8 ..  15]   magic        (NBD_OPTS_MAGIC)
         [16 ..  17]   server flags (0)
-
-       part 2 (after options are sent):
+        ....options sent....
         [18 ..  25]   size
         [26 ..  27]   export flags
         [28 .. 151]   reserved     (0)
      */
 
-    qio_channel_set_blocking(ioc, true, NULL);
+    qio_channel_set_blocking(client->ioc, true, NULL);
     rc = -EINVAL;
 
     TRACE("Beginning negotiation.");
     memset(buf, 0, sizeof(buf));
     memcpy(buf, "NBDMAGIC", 8);
-    if (client->exp) {
+
+    oldStyle = client->exp != NULL && !client->tlscreds;
+    if (oldStyle) {
         assert ((client->exp->nbdflags & ~65535) == 0);
         cpu_to_be64w((uint64_t*)(buf + 8), NBD_CLIENT_MAGIC);
         cpu_to_be64w((uint64_t*)(buf + 16), client->exp->size);
@@ -628,13 +733,17 @@ static int nbd_send_negotiate(NBDClient *client)
         cpu_to_be16w((uint16_t *)(buf + 16), NBD_FLAG_FIXED_NEWSTYLE);
     }
 
-    if (client->exp) {
-        if (write_sync(ioc, buf, sizeof(buf)) != sizeof(buf)) {
+    if (oldStyle) {
+        if (client->tlscreds) {
+            TRACE("TLS cannot be enabled with oldstyle protocol");
+            goto fail;
+        }
+        if (write_sync(client->ioc, buf, sizeof(buf)) != sizeof(buf)) {
             LOG("write failed");
             goto fail;
         }
     } else {
-        if (write_sync(ioc, buf, 18) != 18) {
+        if (write_sync(client->ioc, buf, 18) != 18) {
             LOG("write failed");
             goto fail;
         }
@@ -647,7 +756,8 @@ static int nbd_send_negotiate(NBDClient *client)
         assert ((client->exp->nbdflags & ~65535) == 0);
         cpu_to_be64w((uint64_t*)(buf + 18), client->exp->size);
         cpu_to_be16w((uint16_t*)(buf + 26), client->exp->nbdflags | myflags);
-        if (write_sync(ioc, buf + 18, sizeof(buf) - 18) != sizeof(buf) - 18) {
+        if (write_sync(client->ioc, buf + 18, sizeof(buf) - 18) !=
+            sizeof(buf) - 18) {
             LOG("write failed");
             goto fail;
         }
@@ -656,7 +766,7 @@ static int nbd_send_negotiate(NBDClient *client)
     TRACE("Negotiation succeeded.");
     rc = 0;
 fail:
-    qio_channel_set_blocking(ioc, false, NULL);
+    qio_channel_set_blocking(client->ioc, false, NULL);
     return rc;
 }
 
@@ -676,6 +786,10 @@ static int nbd_handle_reply_err(uint32_t opt, uint32_t type, Error **errp)
         error_setg(errp, "Invalid data length for option %x", opt);
         break;
 
+    case NBD_REP_ERR_TLS_REQD:
+        error_setg(errp, "TLS negotiation required before option %x", opt);
+        break;
+
     default:
         error_setg(errp, "Unknown error code when asking for option %x", opt);
         break;
@@ -834,7 +948,109 @@ static char *nbd_receive_pick_export(QIOChannel *ioc,
     return chosenname;
 }
 
+
+static QIOChannel *nbd_receive_starttls(QIOChannel *ioc,
+                                        QCryptoTLSCreds *tlscreds,
+                                        const char *hostname, Error **errp)
+{
+    uint64_t magic = cpu_to_be64(NBD_OPTS_MAGIC);
+    uint32_t opt = cpu_to_be32(NBD_OPT_STARTTLS);
+    uint32_t length = 0;
+    uint32_t type;
+    QIOChannelTLS *tioc;
+    struct NBDTLSHandshakeData data = { 0 };
+
+    TRACE("Requesting TLS from server");
+    if (write_sync(ioc, &magic, sizeof(magic)) != sizeof(magic)) {
+        error_setg(errp, "Failed to send option magic");
+        return NULL;
+    }
+
+    if (write_sync(ioc, &opt, sizeof(opt)) != sizeof(opt)) {
+        error_setg(errp, "Failed to send option number");
+        return NULL;
+    }
+
+    if (write_sync(ioc, &length, sizeof(length)) != sizeof(length)) {
+        error_setg(errp, "Failed to send option length");
+        return NULL;
+    }
+
+    TRACE("Getting TLS reply from server1");
+    if (read_sync(ioc, &magic, sizeof(magic)) != sizeof(magic)) {
+        error_setg(errp, "failed to read option magic");
+        return NULL;
+    }
+    magic = be64_to_cpu(magic);
+    if (magic != NBD_REP_MAGIC) {
+        error_setg(errp, "Unexpected option magic");
+        return NULL;
+    }
+    TRACE("Getting TLS reply from server2");
+    if (read_sync(ioc, &opt, sizeof(opt)) != sizeof(opt)) {
+        error_setg(errp, "failed to read option");
+        return NULL;
+    }
+    opt = be32_to_cpu(opt);
+    if (opt != NBD_OPT_STARTTLS) {
+        error_setg(errp, "Unexpected option type %x expected %x",
+                   opt, NBD_OPT_STARTTLS);
+        return NULL;
+    }
+
+    TRACE("Getting TLS reply from server");
+    if (read_sync(ioc, &type, sizeof(type)) != sizeof(type)) {
+        error_setg(errp, "failed to read option type");
+        return NULL;
+    }
+    type = be32_to_cpu(type);
+    if (type != NBD_REP_ACK) {
+        error_setg(errp, "Server rejected request to start TLS %x",
+                   type);
+        return NULL;
+    }
+
+    TRACE("Getting TLS reply from server");
+    if (read_sync(ioc, &length, sizeof(length)) != sizeof(length)) {
+        error_setg(errp, "failed to read option length");
+        return NULL;
+    }
+    length = be32_to_cpu(length);
+    if (length != 0) {
+        error_setg(errp, "Start TLS reponse was not zero %x",
+                   length);
+        return NULL;
+    }
+
+    TRACE("TLS request approved, setting up TLS");
+    tioc = qio_channel_tls_new_client(ioc, tlscreds, hostname, errp);
+    if (!tioc) {
+        return NULL;
+    }
+    data.loop = g_main_loop_new(g_main_context_default(), FALSE);
+    TRACE("Starting TLS hanshake");
+    qio_channel_tls_handshake(tioc,
+                              nbd_tls_handshake,
+                              &data,
+                              NULL);
+
+    if (!data.complete) {
+        g_main_loop_run(data.loop);
+    }
+    g_main_loop_unref(data.loop);
+    if (data.error) {
+        error_propagate(errp, data.error);
+        object_unref(OBJECT(tioc));
+        return NULL;
+    }
+
+    return QIO_CHANNEL(tioc);
+}
+
+
 int nbd_receive_negotiate(QIOChannel *ioc, const char *name, uint32_t *flags,
+                          QCryptoTLSCreds *tlscreds, const char *hostname,
+                          QIOChannel **outioc,
                           off_t *size, Error **errp)
 {
     char buf[256];
@@ -842,10 +1058,19 @@ int nbd_receive_negotiate(QIOChannel *ioc, const char *name, uint32_t *flags,
     int rc;
     char *autoname = NULL;
 
-    TRACE("Receiving negotiation.");
+    TRACE("Receiving negotiation tlscreds=%p hostname=%s.",
+          tlscreds, hostname ? hostname : "<null>");
 
     rc = -EINVAL;
 
+    if (outioc) {
+        *outioc = NULL;
+    }
+    if (tlscreds && !outioc) {
+        error_setg(errp, "Output I/O channel required for TLS");
+        goto fail;
+    }
+
     if (read_sync(ioc, buf, 8) != 8) {
         error_setg(errp, "Failed to read data");
         goto fail;
@@ -907,6 +1132,18 @@ int nbd_receive_negotiate(QIOChannel *ioc, const char *name, uint32_t *flags,
             error_setg(errp, "Failed to send clientflags field");
             goto fail;
         }
+        if (tlscreds) {
+            if (fixedNewStyle) {
+                *outioc = nbd_receive_starttls(ioc, tlscreds, hostname, errp);
+                if (!*outioc) {
+                    goto fail;
+                }
+                ioc = *outioc;
+            } else {
+                error_setg(errp, "Server does not support STARTTLS");
+                goto fail;
+            }
+        }
         /* write the export name */
         if (fixedNewStyle) {
             autoname = nbd_receive_pick_export(ioc, name, errp);
@@ -961,6 +1198,10 @@ int nbd_receive_negotiate(QIOChannel *ioc, const char *name, uint32_t *flags,
             error_setg(errp, "Server does not support export names");
             goto fail;
         }
+        if (tlscreds) {
+            error_setg(errp, "Server does not support STARTTLS");
+            goto fail;
+        }
 
         if (read_sync(ioc, &s, sizeof(s)) != sizeof(s)) {
             error_setg(errp, "Failed to read export length");
@@ -1247,6 +1488,10 @@ void nbd_client_put(NBDClient *client)
         nbd_unset_handlers(client);
         object_unref(OBJECT(client->sioc));
         object_unref(OBJECT(client->ioc));
+        if (client->tlscreds) {
+            object_unref(OBJECT(client->tlscreds));
+        }
+        g_free(client->tlsaclname);
         if (client->exp) {
             QTAILQ_REMOVE(&client->exp->clients, client, next);
             nbd_export_put(client->exp);
@@ -1757,12 +2002,19 @@ static void nbd_update_can_read(NBDClient *client)
 
 NBDClient *nbd_client_new(NBDExport *exp,
                           QIOChannelSocket *sioc,
+                          QCryptoTLSCreds *tlscreds,
+                          const char *tlsaclname,
                           void (*close)(NBDClient *))
 {
     NBDClient *client;
     client = g_malloc0(sizeof(NBDClient));
     client->refcount = 1;
     client->exp = exp;
+    client->tlscreds = tlscreds;
+    if (tlscreds) {
+        object_ref(OBJECT(client->tlscreds));
+    }
+    client->tlsaclname = g_strdup(tlsaclname);
     client->sioc = sioc;
     object_ref(OBJECT(client->sioc));
     client->ioc = QIO_CHANNEL(sioc);
@@ -1771,6 +2023,10 @@ NBDClient *nbd_client_new(NBDExport *exp,
     if (nbd_send_negotiate(client)) {
         object_unref(OBJECT(client->sioc));
         object_unref(OBJECT(client->ioc));
+        if (client->tlscreds) {
+            object_unref(OBJECT(client->tlscreds));
+        }
+        g_free(client->tlsaclname);
         g_free(client);
         return NULL;
     }
diff --git a/qemu-nbd.c b/qemu-nbd.c
index 3f7f2222..24cdb65 100644
--- a/qemu-nbd.c
+++ b/qemu-nbd.c
@@ -254,6 +254,7 @@ static void *nbd_client_thread(void *arg)
     }
 
     ret = nbd_receive_negotiate(QIO_CHANNEL(sioc), NULL, &nbdflags,
+                                NULL, NULL, NULL,
                                 &size, &local_error);
     if (ret < 0) {
         if (local_error) {
@@ -342,7 +343,8 @@ static gboolean nbd_accept(QIOChannel *ioc, GIOCondition cond, gpointer opaque)
         return TRUE;
     }
 
-    if (nbd_client_new(newproto ? NULL : exp, cioc, nbd_client_closed)) {
+    if (nbd_client_new(newproto ? NULL : exp, cioc,
+                       NULL, NULL, nbd_client_closed)) {
         nb_fds++;
         nbd_update_server_watch();
     }
-- 
2.5.0

^ permalink raw reply related	[flat|nested] 23+ messages in thread

* [Qemu-devel] [PATCH 13/15] nbd: enable use of TLS with NBD block driver
  2015-11-27 12:20 [Qemu-devel] [PATCH 00/15] Implement TLS support to QEMU NBD server & client Daniel P. Berrange
                   ` (11 preceding siblings ...)
  2015-11-27 12:20 ` [Qemu-devel] [PATCH 12/15] nbd: implement TLS support in the protocol negotiation Daniel P. Berrange
@ 2015-11-27 12:20 ` Daniel P. Berrange
  2015-11-27 12:20 ` [Qemu-devel] [PATCH 14/15] nbd: enable use of TLS with qemu-nbd server Daniel P. Berrange
                   ` (3 subsequent siblings)
  16 siblings, 0 replies; 23+ messages in thread
From: Daniel P. Berrange @ 2015-11-27 12:20 UTC (permalink / raw)
  To: qemu-devel; +Cc: Paolo Bonzini, qemu-block, Wouter Verhelst

This modifies the NBD driver so that it is possible to request
use of TLS. This is done by providing the 'tls-creds' parameter
with the ID of a previously created QCryptoTLSCreds object.

For example

  $QEMU -object tls-creds-x509,id=tls0,endpoint=client,\
                dir=/home/berrange/security/qemutls \
        -drive driver=nbd,host=localhost,port=9000,tls-creds=tls0

The client will drop the connection if the NBD server does not
provide TLS.

Signed-off-by: Daniel P. Berrange <berrange@redhat.com>
---
 block/nbd-client.c | 10 ++++---
 block/nbd-client.h |  2 ++
 block/nbd.c        | 78 +++++++++++++++++++++++++++++++++++++++++++++++-------
 3 files changed, 78 insertions(+), 12 deletions(-)

diff --git a/block/nbd-client.c b/block/nbd-client.c
index f7bacfd..024d5f3 100644
--- a/block/nbd-client.c
+++ b/block/nbd-client.c
@@ -378,8 +378,12 @@ void nbd_client_close(BlockDriverState *bs)
     nbd_teardown_connection(bs);
 }
 
-int nbd_client_init(BlockDriverState *bs, QIOChannelSocket *sioc,
-                    const char *export, Error **errp)
+int nbd_client_init(BlockDriverState *bs,
+                    QIOChannelSocket *sioc,
+                    const char *export,
+                    QCryptoTLSCreds *tlscreds,
+                    const char *hostname,
+                    Error **errp)
 {
     NbdClientSession *client = nbd_get_client_session(bs);
     int ret;
@@ -390,7 +394,7 @@ int nbd_client_init(BlockDriverState *bs, QIOChannelSocket *sioc,
 
     ret = nbd_receive_negotiate(QIO_CHANNEL(sioc), export,
                                 &client->nbdflags,
-                                NULL, NULL,
+                                tlscreds, hostname,
                                 &client->ioc,
                                 &client->size, errp);
     if (ret < 0) {
diff --git a/block/nbd-client.h b/block/nbd-client.h
index e8b3283..53f116d 100644
--- a/block/nbd-client.h
+++ b/block/nbd-client.h
@@ -39,6 +39,8 @@ NbdClientSession *nbd_get_client_session(BlockDriverState *bs);
 int nbd_client_init(BlockDriverState *bs,
                     QIOChannelSocket *sock,
                     const char *export_name,
+                    QCryptoTLSCreds *tlscreds,
+                    const char *hostname,
                     Error **errp);
 void nbd_client_close(BlockDriverState *bs);
 
diff --git a/block/nbd.c b/block/nbd.c
index e51acfc..5f7061e 100644
--- a/block/nbd.c
+++ b/block/nbd.c
@@ -259,36 +259,92 @@ static QIOChannelSocket *nbd_establish_connection(SocketAddress *saddr,
     return sioc;
 }
 
+
+static QCryptoTLSCreds *nbd_get_tls_creds(const char *id, Error **errp)
+{
+    Object *obj;
+    QCryptoTLSCreds *creds;
+
+    obj = object_resolve_path_component(
+        object_get_objects_root(), id);
+    if (!obj) {
+        error_setg(errp, "No TLS credentials with id '%s'",
+                   id);
+        return NULL;
+    }
+    creds = (QCryptoTLSCreds *)
+        object_dynamic_cast(obj, TYPE_QCRYPTO_TLS_CREDS);
+    if (!creds) {
+        error_setg(errp, "Object with id '%s' is not TLS credentials",
+                   id);
+        return NULL;
+    }
+
+    if (creds->endpoint != QCRYPTO_TLS_CREDS_ENDPOINT_CLIENT) {
+        error_setg(errp,
+                   "Expecting TLS credentials with a client endpoint");
+        return NULL;
+    }
+    object_ref(obj);
+    return creds;
+}
+
+
 static int nbd_open(BlockDriverState *bs, QDict *options, int flags,
                     Error **errp)
 {
     BDRVNBDState *s = bs->opaque;
     char *export = NULL;
-    int result;
-    QIOChannelSocket *sioc;
+    QIOChannelSocket *sioc = NULL;
     SocketAddress *saddr;
+    const char *tlscredsid;
+    QCryptoTLSCreds *tlscreds = NULL;
+    const char *hostname = NULL;
+    int ret = -EINVAL;
 
     /* Pop the config into our state object. Exit if invalid. */
     saddr = nbd_config(s, options, &export, errp);
     if (!saddr) {
-        return -EINVAL;
+        goto error;
+    }
+
+    tlscredsid = g_strdup(qdict_get_try_str(options, "tls-creds"));
+    if (tlscredsid) {
+        qdict_del(options, "tls-creds");
+        tlscreds = nbd_get_tls_creds(tlscredsid, errp);
+        if (!tlscreds) {
+            goto error;
+        }
+
+        if (saddr->type != SOCKET_ADDRESS_KIND_INET) {
+            error_setg(errp, "TLS only supported over IP sockets");
+            goto error;
+        }
+        hostname = saddr->u.inet->host;
     }
 
     /* establish TCP connection, return error if it fails
      * TODO: Configurable retry-until-timeout behaviour.
      */
     sioc = nbd_establish_connection(saddr, errp);
-    qapi_free_SocketAddress(saddr);
     if (!sioc) {
-        g_free(export);
-        return -ECONNREFUSED;
+        ret = -ECONNREFUSED;
+        goto error;
     }
 
     /* NBD handshake */
-    result = nbd_client_init(bs, sioc, export, errp);
-    object_unref(OBJECT(sioc));
+    ret = nbd_client_init(bs, sioc, export,
+                          tlscreds, hostname, errp);
+ error:
+    if (sioc) {
+        object_unref(OBJECT(sioc));
+    }
+    if (tlscreds) {
+        object_unref(OBJECT(tlscreds));
+    }
+    qapi_free_SocketAddress(saddr);
     g_free(export);
-    return result;
+    return ret;
 }
 
 static int nbd_co_readv(BlockDriverState *bs, int64_t sector_num,
@@ -350,6 +406,7 @@ static void nbd_refresh_filename(BlockDriverState *bs)
     const char *host   = qdict_get_try_str(bs->options, "host");
     const char *port   = qdict_get_try_str(bs->options, "port");
     const char *export = qdict_get_try_str(bs->options, "export");
+    const char *tlscreds = qdict_get_try_str(bs->options, "tls-creds");
 
     qdict_put_obj(opts, "driver", QOBJECT(qstring_from_str("nbd")));
 
@@ -384,6 +441,9 @@ static void nbd_refresh_filename(BlockDriverState *bs)
     if (export) {
         qdict_put_obj(opts, "export", QOBJECT(qstring_from_str(export)));
     }
+    if (tlscreds) {
+        qdict_put_obj(opts, "tls-creds", QOBJECT(qstring_from_str(tlscreds)));
+    }
 
     bs->full_open_options = opts;
 }
-- 
2.5.0

^ permalink raw reply related	[flat|nested] 23+ messages in thread

* [Qemu-devel] [PATCH 14/15] nbd: enable use of TLS with qemu-nbd server
  2015-11-27 12:20 [Qemu-devel] [PATCH 00/15] Implement TLS support to QEMU NBD server & client Daniel P. Berrange
                   ` (12 preceding siblings ...)
  2015-11-27 12:20 ` [Qemu-devel] [PATCH 13/15] nbd: enable use of TLS with NBD block driver Daniel P. Berrange
@ 2015-11-27 12:20 ` Daniel P. Berrange
  2015-11-27 12:20 ` [Qemu-devel] [PATCH 15/15] nbd: enable use of TLS with nbd-server-start command Daniel P. Berrange
                   ` (2 subsequent siblings)
  16 siblings, 0 replies; 23+ messages in thread
From: Daniel P. Berrange @ 2015-11-27 12:20 UTC (permalink / raw)
  To: qemu-devel; +Cc: Paolo Bonzini, qemu-block, Wouter Verhelst

This modifies the qemu-nbd program so that it is possible to
request the use of TLS with the server. It simply adds a new
command line option --tls-creds which is used to provide the
ID of a QCryptoTLSCreds object previously created via the
--object command line option.

For example

  qemu-nbd --object tls-creds-x509,id=tls0,endpoint=server,\
                    dir=/home/berrange/security/qemutls \
           --tls-creds tls0
	   --exportname default

Note that it is mandatory to supply the --exportname argument
when requesting TLS, since it requires use of the new style
NBD protocol where the client requests a volume name explicitly.

TLS is only supported when using an IPv4/IPv6 socket listener.
It is not possible to use with UNIX sockets, which includes
when connecting the NBD server to a host device.

Signed-off-by: Daniel P. Berrange <berrange@redhat.com>
---
 qemu-nbd.c    | 57 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++-
 qemu-nbd.texi |  4 ++++
 2 files changed, 60 insertions(+), 1 deletion(-)

diff --git a/qemu-nbd.c b/qemu-nbd.c
index 24cdb65..e579188 100644
--- a/qemu-nbd.c
+++ b/qemu-nbd.c
@@ -45,6 +45,7 @@
 #define QEMU_NBD_OPT_DISCARD       3
 #define QEMU_NBD_OPT_DETECT_ZEROES 4
 #define QEMU_NBD_OPT_OBJECT        5
+#define QEMU_NBD_OPT_TLSCREDS      6
 
 static NBDExport *exp;
 static bool newproto;
@@ -57,6 +58,7 @@ static int shared = 1;
 static int nb_fds;
 static QIOChannelSocket *server_ioc;
 static int server_watch = -1;
+static QCryptoTLSCreds *tlscreds;
 
 static void usage(const char *name)
 {
@@ -344,7 +346,7 @@ static gboolean nbd_accept(QIOChannel *ioc, GIOCondition cond, gpointer opaque)
     }
 
     if (nbd_client_new(newproto ? NULL : exp, cioc,
-                       NULL, NULL, nbd_client_closed)) {
+                       tlscreds, NULL, nbd_client_closed)) {
         nb_fds++;
         nbd_update_server_watch();
     }
@@ -458,6 +460,37 @@ out:
     return 0;
 }
 
+
+static QCryptoTLSCreds *nbd_get_tls_creds(const char *id, Error **errp)
+{
+    Object *obj;
+    QCryptoTLSCreds *creds;
+
+    obj = object_resolve_path_component(
+        object_get_objects_root(), id);
+    if (!obj) {
+        error_setg(errp, "No TLS credentials with id '%s'",
+                   id);
+        return NULL;
+    }
+    creds = (QCryptoTLSCreds *)
+        object_dynamic_cast(obj, TYPE_QCRYPTO_TLS_CREDS);
+    if (!creds) {
+        error_setg(errp, "Object with id '%s' is not TLS credentials",
+                   id);
+        return NULL;
+    }
+
+    if (creds->endpoint != QCRYPTO_TLS_CREDS_ENDPOINT_SERVER) {
+        error_setg(errp,
+                   "Expecting TLS credentials with a server endpoint");
+        return NULL;
+    }
+    object_ref(obj);
+    return creds;
+}
+
+
 int main(int argc, char **argv)
 {
     BlockBackend *blk;
@@ -497,6 +530,7 @@ int main(int argc, char **argv)
         { "verbose", 0, NULL, 'v' },
         { "object", 1, NULL, QEMU_NBD_OPT_OBJECT },
         { "exportname", 1, NULL, 'x' },
+        { "tls-creds", 1, NULL, QEMU_NBD_OPT_TLSCREDS },
         { NULL, 0, NULL, 0 }
     };
     int ch;
@@ -515,6 +549,7 @@ int main(int argc, char **argv)
     QDict *options = NULL;
     QemuOpts *opts;
     const char *exportname = NULL;
+    const char *tlscredsid = NULL;
 
     /* The client thread uses SIGTERM to interrupt the server.  A signal
      * handler ensures that "qemu-nbd -v -c" exits with a nice status code.
@@ -671,6 +706,9 @@ int main(int argc, char **argv)
                 exit(1);
             }
             break;
+        case QEMU_NBD_OPT_TLSCREDS:
+            tlscredsid = optarg;
+            break;
         case '?':
             errx(EXIT_FAILURE, "Try `%s --help' for more information.",
                  argv[0]);
@@ -689,6 +727,23 @@ int main(int argc, char **argv)
         exit(1);
     }
 
+    if (tlscredsid) {
+        if (!exportname) {
+            errx(EXIT_FAILURE, "Export name is required when using TLS");
+        }
+        if (sockpath) {
+            errx(EXIT_FAILURE, "TLS is only supported with IPv4/IPv6");
+        }
+        if (device) {
+            errx(EXIT_FAILURE, "TLS is not supported with a host device");
+        }
+        tlscreds = nbd_get_tls_creds(tlscredsid, &local_err);
+        if (local_err) {
+            errx(EXIT_FAILURE, "Failed to get TLS creds %s",
+                 error_get_pretty(local_err));
+        }
+    }
+
     if (disconnect) {
         int nbdfd = open(argv[optind], O_RDWR);
         if (nbdfd < 0) {
diff --git a/qemu-nbd.texi b/qemu-nbd.texi
index 6ff3920..a12d2e1 100644
--- a/qemu-nbd.texi
+++ b/qemu-nbd.texi
@@ -67,6 +67,10 @@ Export QEMU disk image using NBD protocol.
 @item -x NAME, --exportname=NAME
   set the NDB volume export name. This switches the server to use
   the new style NBD protocol negotiation
+@item --tls-creds=ID
+  enable mandatory TLS encryption for the server by setting the ID
+  of the TLS credentials object previously created with the --object
+  option.
 @item -v, --verbose
   display extra debugging information
 @item -h, --help
-- 
2.5.0

^ permalink raw reply related	[flat|nested] 23+ messages in thread

* [Qemu-devel] [PATCH 15/15] nbd: enable use of TLS with nbd-server-start command
  2015-11-27 12:20 [Qemu-devel] [PATCH 00/15] Implement TLS support to QEMU NBD server & client Daniel P. Berrange
                   ` (13 preceding siblings ...)
  2015-11-27 12:20 ` [Qemu-devel] [PATCH 14/15] nbd: enable use of TLS with qemu-nbd server Daniel P. Berrange
@ 2015-11-27 12:20 ` Daniel P. Berrange
  2015-11-27 14:06 ` [Qemu-devel] [PATCH 00/15] Implement TLS support to QEMU NBD server & client Wouter Verhelst
  2015-12-02 12:56 ` Wouter Verhelst
  16 siblings, 0 replies; 23+ messages in thread
From: Daniel P. Berrange @ 2015-11-27 12:20 UTC (permalink / raw)
  To: qemu-devel; +Cc: Paolo Bonzini, qemu-block, Wouter Verhelst

This modifies the nbd-server-start QMP command so that it
is possible to request use of TLS. This is done by adding
a new optional parameter "tls-creds" which provides the ID
of a previously created QCryptoTLSCreds object instance.

TLS is only supported when using an IPv4/IPv6 socket listener.

Signed-off-by: Daniel P. Berrange <berrange@redhat.com>
---
 blockdev-nbd.c  | 122 ++++++++++++++++++++++++++++++++++++++++++++++----------
 hmp.c           |   2 +-
 qapi/block.json |   4 +-
 qmp-commands.hx |   2 +-
 4 files changed, 105 insertions(+), 25 deletions(-)

diff --git a/blockdev-nbd.c b/blockdev-nbd.c
index 6af1684..e287f05 100644
--- a/blockdev-nbd.c
+++ b/blockdev-nbd.c
@@ -19,42 +19,126 @@
 #include "block/nbd.h"
 #include "io/channel-socket.h"
 
-static QIOChannelSocket *server_ioc;
-static int server_watch = -1;
+typedef struct NBDServerData {
+    QIOChannelSocket *listen_ioc;
+    int watch;
+    QCryptoTLSCreds *tlscreds;
+} NBDServerData;
+
+static NBDServerData *nbd_server;
+
 
 static gboolean nbd_accept(QIOChannel *ioc, GIOCondition condition,
                            gpointer opaque)
 {
     QIOChannelSocket *cioc;
 
+    if (!nbd_server) {
+        return FALSE;
+    }
+
     cioc = qio_channel_socket_accept(QIO_CHANNEL_SOCKET(ioc),
                                      NULL);
     if (!cioc) {
         return TRUE;
     }
 
-    nbd_client_new(NULL, cioc, NULL, NULL, nbd_client_put);
+    nbd_client_new(NULL, cioc,
+                   nbd_server->tlscreds, NULL,
+                   nbd_client_put);
     object_unref(OBJECT(cioc));
     return TRUE;
 }
 
-void qmp_nbd_server_start(SocketAddress *addr, Error **errp)
+
+static void nbd_server_free(NBDServerData *server)
 {
-    if (server_ioc) {
-        error_setg(errp, "NBD server already running");
+    if (!server) {
         return;
     }
 
-    server_ioc = qio_channel_socket_new();
-    if (qio_channel_socket_listen_sync(server_ioc, addr, errp) < 0) {
+    if (server->watch != -1) {
+        g_source_remove(server->watch);
+    }
+    object_unref(OBJECT(server->listen_ioc));
+    if (server->tlscreds) {
+        object_unref(OBJECT(server->tlscreds));
+    }
+
+    g_free(server);
+}
+
+static QCryptoTLSCreds *nbd_get_tls_creds(const char *id, Error **errp)
+{
+    Object *obj;
+    QCryptoTLSCreds *creds;
+
+    obj = object_resolve_path_component(
+        object_get_objects_root(), id);
+    if (!obj) {
+        error_setg(errp, "No TLS credentials with id '%s'",
+                   id);
+        return NULL;
+    }
+    creds = (QCryptoTLSCreds *)
+        object_dynamic_cast(obj, TYPE_QCRYPTO_TLS_CREDS);
+    if (!creds) {
+        error_setg(errp, "Object with id '%s' is not TLS credentials",
+                   id);
+        return NULL;
+    }
+
+    if (creds->endpoint != QCRYPTO_TLS_CREDS_ENDPOINT_SERVER) {
+        error_setg(errp,
+                   "Expecting TLS credentials with a server endpoint");
+        return NULL;
+    }
+    object_ref(obj);
+    return creds;
+}
+
+
+void qmp_nbd_server_start(SocketAddress *addr,
+                          bool has_tls_creds, const char *tls_creds,
+                          Error **errp)
+{
+    if (nbd_server) {
+        error_setg(errp, "NBD server already running");
         return;
     }
 
-    server_watch = qio_channel_add_watch(QIO_CHANNEL(server_ioc),
-                                         G_IO_IN,
-                                         nbd_accept,
-                                         NULL,
-                                         NULL);
+    nbd_server = g_new0(NBDServerData, 1);
+    nbd_server->watch = -1;
+    nbd_server->listen_ioc = qio_channel_socket_new();
+    if (qio_channel_socket_listen_sync(
+            nbd_server->listen_ioc, addr, errp) < 0) {
+        goto error;
+    }
+
+    if (has_tls_creds) {
+        nbd_server->tlscreds = nbd_get_tls_creds(tls_creds, errp);
+        if (!nbd_server->tlscreds) {
+            goto error;
+        }
+
+        if (addr->type != SOCKET_ADDRESS_KIND_INET) {
+            error_setg(errp, "TLS is only supported with IPv4/IPv6");
+            goto error;
+        }
+    }
+
+    nbd_server->watch = qio_channel_add_watch(
+        QIO_CHANNEL(nbd_server->listen_ioc),
+        G_IO_IN,
+        nbd_accept,
+        NULL,
+        NULL);
+
+    return;
+
+ error:
+    nbd_server_free(nbd_server);
+    nbd_server = NULL;
 }
 
 /*
@@ -89,7 +173,7 @@ void qmp_nbd_server_add(const char *device, bool has_writable, bool writable,
     NBDExport *exp;
     NBDCloseNotifier *n;
 
-    if (!server_ioc) {
+    if (!nbd_server) {
         error_setg(errp, "NBD server not running");
         return;
     }
@@ -139,12 +223,6 @@ void qmp_nbd_server_stop(Error **errp)
         nbd_close_notifier(&cn->n, nbd_export_get_blockdev(cn->exp));
     }
 
-    if (server_watch != -1) {
-        g_source_remove(server_watch);
-        server_watch = -1;
-    }
-    if (server_ioc) {
-        object_unref(OBJECT(server_ioc));
-        server_ioc = NULL;
-    }
+    nbd_server_free(nbd_server);
+    nbd_server = NULL;
 }
diff --git a/hmp.c b/hmp.c
index 6044db3..89a37cf 100644
--- a/hmp.c
+++ b/hmp.c
@@ -1830,7 +1830,7 @@ void hmp_nbd_server_start(Monitor *mon, const QDict *qdict)
         goto exit;
     }
 
-    qmp_nbd_server_start(addr, &local_err);
+    qmp_nbd_server_start(addr, false, NULL, &local_err);
     qapi_free_SocketAddress(addr);
     if (local_err != NULL) {
         goto exit;
diff --git a/qapi/block.json b/qapi/block.json
index 84022f1..876bac4 100644
--- a/qapi/block.json
+++ b/qapi/block.json
@@ -130,13 +130,15 @@
 # QEMU instance could refer to them as "nbd:HOST:PORT:exportname=NAME".
 #
 # @addr: Address on which to listen.
+# @tls-creds: (optional) ID of the TLS credentials object. Since 2.6
 #
 # Returns: error if the server is already running.
 #
 # Since: 1.3.0
 ##
 { 'command': 'nbd-server-start',
-  'data': { 'addr': 'SocketAddress' } }
+  'data': { 'addr': 'SocketAddress',
+            '*tls-creds': 'str'} }
 
 ##
 # @nbd-server-add:
diff --git a/qmp-commands.hx b/qmp-commands.hx
index 9d8b42f..fd353e6 100644
--- a/qmp-commands.hx
+++ b/qmp-commands.hx
@@ -3752,7 +3752,7 @@ EQMP
 
     {
         .name       = "nbd-server-start",
-        .args_type  = "addr:q",
+        .args_type  = "addr:q,tls-creds:s?",
         .mhandler.cmd_new = qmp_marshal_nbd_server_start,
     },
     {
-- 
2.5.0

^ permalink raw reply related	[flat|nested] 23+ messages in thread

* Re: [Qemu-devel] [PATCH 00/15] Implement TLS support to QEMU NBD server & client
  2015-11-27 12:20 [Qemu-devel] [PATCH 00/15] Implement TLS support to QEMU NBD server & client Daniel P. Berrange
                   ` (14 preceding siblings ...)
  2015-11-27 12:20 ` [Qemu-devel] [PATCH 15/15] nbd: enable use of TLS with nbd-server-start command Daniel P. Berrange
@ 2015-11-27 14:06 ` Wouter Verhelst
  2015-12-03 14:53   ` Wouter Verhelst
  2015-12-02 12:56 ` Wouter Verhelst
  16 siblings, 1 reply; 23+ messages in thread
From: Wouter Verhelst @ 2015-11-27 14:06 UTC (permalink / raw)
  To: Daniel P. Berrange; +Cc: nbd-general, Paolo Bonzini, qemu-devel, qemu-block

[nbd-general added to Cc]

Hi Daniel,

On Fri, Nov 27, 2015 at 12:20:38PM +0000, Daniel P. Berrange wrote:
> This series of patches implements support for TLS in the QEMU NBD
> server and client code.
> 
> It is implementing the NBD_OPT_STARTTLS option that was previously
> discussed here:
> 
>   https://www.redhat.com/archives/libvir-list/2014-October/msg00506.html
> 
> And is also described in the NBD spec here:
> 
>   https://github.com/yoe/nbd/blob/master/doc/proto.md
> 
> Based on my impl I think the NBD_OPT_STARTTLS specification is
> fine. In my impl, when TLS is requested in the server, I chose
> to *refuse* all NBD options until NBD_OPT_STARTTLS has completed
> successfully. There is an annoying thing about the fixed new style
> protocol in that it made an exception for NBD_OPT_EXPORT_NAME,
> so that it is still not possible to report errors for that option.
> 
> [qupte]
>  - The server will reply to any option apart from `NBD_OPT_EXPORT_NAME`
>     with reply packets in the following format:
> 
>   S: 64 bits, `0x3e889045565a9` (magic number for replies)  
>   S: 32 bits, the option as sent by the client to which this is a reply  
>   S: 32 bits, reply type (e.g., `NBD_REP_ACK` for successful completion,
>      or `NBD_REP_ERR_UNSUP` to mark use of an option not known by this
>      server  
>   S: 32 bits, length of the reply. This may be zero for some replies, in
>      which case the next field is not sent  
>   S: any data as required by the reply (e.g., an export name in the case
>      of `NBD_REP_SERVER`)
> [/quote]
> 
> I wish those words "apart from NBD_OPT_EXPORT_NAME" were not there
> for fixed new style protocol - maybe it needs a very-fixed new
> style protocol...

Yes. This was done in this way to more easily retain backwards compatibility
with non-fixed newstyle. In hindsight, it was probably a mistake to do
things that way, but it's not going to be easy to turn back the clock
now...

I have been thinking of adding a message NBD_OPT_SELECT_EXPORT to
replace NBD_OPT_EXPORT_NAME, which would select an export but not end
negotiation. That would also require another message to end negotiation
and move on to the transmission phase, obviously.

The negotation would then look something like:

NBD_OPT_SELECT_EXPORT
 <- NBD_REP_ACK
NBD_OPT_GO
 <- NBD_REP_ACK
[transmission phase]

or, to add some error conditions:

NBD_OPT_SELECT_EXPORT
 <- NBD_REP_ERR_UNSUP # this server doesn't support SELECT_EXPORT
NBD_OPT_EXPORT_NAME # fall back to older message
[transmission phase]

NBD_OPT_SELECT_EXPORT
 <- NBD_REP_ERR_TLS_REQD # this export requires TLS
NBD_OPT_STARTTLS
 <- NBD_REP_ACK
[... tls ...]
NBD_OPT_SELECT_EXPORT
 <- NBD_REP_ACK
NBD_OPT_GO
 <- NBD_REP_ACK
[transmission phase]

NBD_OPT_SELECT_EXPORT
 <- NBD_REP_ACK
NBD_OPT_STARTTLS
 <- NBD_REP_ACK
[... tls ...]
NBD_OPT_GO
 <- NBD_REP_ERR_INVALID # export selected before starttls is not retained

NBD_OPT_SELECT_EXPORT
 <- NBD_REP_ACK
NBD_OPT_SELECT_EXPORT (we actually want this other one)
 <- NBD_REP_ERR_UNKNOWN # unknown export
NBD_OPT_GO
 <- NBD_REP_ERR_INVALID # second select cleared the result of the first one

etc

I'm not sure whether it's worth the trouble, though.

> As is, if the client connects to a TLS enabled NBD server and then
> immediately sends NBD_OPT_EXPORT_NAME, it is not possible for us
> to send back NBD_REP_ERR_TLS_REQD as the spec requires that the
> server close the connection :-(  For this reason I have made the
> qemu NBD client always send NBD_OPT_LIST as the first thing it
> does, so that we can see the NBD_REP_ERR_TLS_REQD response.

I suppose that is a valid workaround.

> I chose to *NOT* implement the NBD_OPT_PEEK_EXPORT option as it is
> inherently insecure to have a client to ask the server which
> exports need TLS, while the protocol is still running in plain text
> mode, as it allows a trivial MITM downgrade attack. I can see value
> in the NBD_OPT_PEEK_EXPORT option for probing other features, but
> only after TLS has been negotiated. As such I believe NBD_F_EXP_TLS_OK
> and NBD_F_EXP_TLS_REQ should be deleted from the spec as it is not
> possible to use them in a secure manner.

Mm. Fair enough.

The reason for adding that flag was to support encrypted and
non-encrypted exports from the same server, but I suppose we can signal
those things with NBD_REP_ERR_TLS_REQD and NBD_REP_ERR_POLICY, too.

> This series of patches has my v3 I/O channels patch series as a
> pre-requisite:
> 
>   https://lists.gnu.org/archive/html/qemu-devel/2015-11/msg04208.html
> 
> Usage of TLS is described in the commit messages for each patch,
> but for sake of people who don't want to explore the series, here's
> the summary
> 
> Starting QEMU system emulator with a disk backed by an TLS encrypted
> NBD export
> 
>   $ qemu-system-x86_64 \
>      -object tls-creds-x509,id=tls0,endpoint=client,dir=/home/berrange/security/qemutls
>      -drive driver=nbd,host=localhost,port=9000,tls-creds=tls0
> 
> Starting a standalone NBD server providing a TLS encrypted NBD export
> 
>   $ qemu-nbd \
>      --object tls-creds-x509,id=tls0,endpoint=server,dir=/home/berrange/security/qemutls
>      --tls-creds tls0 \
>      --exportname default
> 
> NB, exportname is mandatory, since TLS requires the fixed-new style
> NBD protocol negotiation.

Note, I have modified the spec of the fixed newstyle negotiation a while
back to include support for a default export:

'A special, "empty", name (i.e., the length field is zero and no name is
specified), is reserved for a "default" export, to be used in cases
where explicitly specifying an export name makes no sense.'

(in the "Option types" section for NBD_OPT_EXPORT_NAME)

This removes the requirement to have an export name specified
externally. Having said that though, obviously adding support for
specifying a name isn't a bad thing.

-- 
It is easy to love a country that is famous for chocolate and beer

  -- Barack Obama, speaking in Brussels, Belgium, 2014-03-26

^ permalink raw reply	[flat|nested] 23+ messages in thread

* Re: [Qemu-devel] [PATCH 12/15] nbd: implement TLS support in the protocol negotiation
  2015-11-27 12:20 ` [Qemu-devel] [PATCH 12/15] nbd: implement TLS support in the protocol negotiation Daniel P. Berrange
@ 2015-11-28 10:28   ` Wouter Verhelst
  2015-12-02 10:45     ` Daniel P. Berrange
  0 siblings, 1 reply; 23+ messages in thread
From: Wouter Verhelst @ 2015-11-28 10:28 UTC (permalink / raw)
  To: Daniel P. Berrange; +Cc: Paolo Bonzini, qemu-devel, qemu-block

Minor nitpick:

On Fri, Nov 27, 2015 at 12:20:50PM +0000, Daniel P. Berrange wrote:
[...]
> @@ -563,6 +659,14 @@ static int nbd_receive_options(NBDClient *client)
>              case NBD_OPT_EXPORT_NAME:
>                  return nbd_handle_export_name(client, length);
>  
> +            case NBD_OPT_STARTTLS:
> +                if (client->tlscreds) {
> +                    TRACE("TLS already enabled");
> +                } else {
> +                    TRACE("TLS not configured");
> +                }
> +                nbd_send_rep(client->ioc, NBD_REP_ERR_UNSUP, clientflags);

NBD_REP_ERR_UNSUP is supposed to be reserved as the default reply for
replies unknown to a server implementation (i.e., it's "this request is
not supported by this server"). Trying to negotiate TLS in a TLS channel
would be NBD_REP_ERR_INVALID ("invalid request"). Trying to negotiate
TLS when no TLS configuration is available server-side would be
NBD_REP_ERR_POLICY ("request not allowed by server-side policy").

Keeping to these error codes would allow a client to provide more useful
information to a user beyond "haha it fail"; but I suppose there can be
arguments for not doing so, too.

Beyond this and the default export that I talked about earlier, no
comments.

-- 
It is easy to love a country that is famous for chocolate and beer

  -- Barack Obama, speaking in Brussels, Belgium, 2014-03-26

^ permalink raw reply	[flat|nested] 23+ messages in thread

* Re: [Qemu-devel] [PATCH 12/15] nbd: implement TLS support in the protocol negotiation
  2015-11-28 10:28   ` Wouter Verhelst
@ 2015-12-02 10:45     ` Daniel P. Berrange
  0 siblings, 0 replies; 23+ messages in thread
From: Daniel P. Berrange @ 2015-12-02 10:45 UTC (permalink / raw)
  To: Wouter Verhelst; +Cc: Paolo Bonzini, qemu-devel, qemu-block

On Sat, Nov 28, 2015 at 11:28:55AM +0100, Wouter Verhelst wrote:
> Minor nitpick:
> 
> On Fri, Nov 27, 2015 at 12:20:50PM +0000, Daniel P. Berrange wrote:
> [...]
> > @@ -563,6 +659,14 @@ static int nbd_receive_options(NBDClient *client)
> >              case NBD_OPT_EXPORT_NAME:
> >                  return nbd_handle_export_name(client, length);
> >  
> > +            case NBD_OPT_STARTTLS:
> > +                if (client->tlscreds) {
> > +                    TRACE("TLS already enabled");
> > +                } else {
> > +                    TRACE("TLS not configured");
> > +                }
> > +                nbd_send_rep(client->ioc, NBD_REP_ERR_UNSUP, clientflags);
> 
> NBD_REP_ERR_UNSUP is supposed to be reserved as the default reply for
> replies unknown to a server implementation (i.e., it's "this request is
> not supported by this server"). Trying to negotiate TLS in a TLS channel
> would be NBD_REP_ERR_INVALID ("invalid request"). Trying to negotiate
> TLS when no TLS configuration is available server-side would be
> NBD_REP_ERR_POLICY ("request not allowed by server-side policy").

Yep that makes sense.

> Beyond this and the default export that I talked about earlier, no
> comments.

Ok, thanks for taking the time to look at this.

Regards,
Daniel
-- 
|: 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 :|

^ permalink raw reply	[flat|nested] 23+ messages in thread

* Re: [Qemu-devel] [PATCH 00/15] Implement TLS support to QEMU NBD server & client
  2015-11-27 12:20 [Qemu-devel] [PATCH 00/15] Implement TLS support to QEMU NBD server & client Daniel P. Berrange
                   ` (15 preceding siblings ...)
  2015-11-27 14:06 ` [Qemu-devel] [PATCH 00/15] Implement TLS support to QEMU NBD server & client Wouter Verhelst
@ 2015-12-02 12:56 ` Wouter Verhelst
  2015-12-02 13:37   ` Daniel P. Berrange
  16 siblings, 1 reply; 23+ messages in thread
From: Wouter Verhelst @ 2015-12-02 12:56 UTC (permalink / raw)
  To: Daniel P. Berrange; +Cc: Paolo Bonzini, qemu-devel, qemu-block

Hi Daniel,

Something occurred to me earlier today:

On Fri, Nov 27, 2015 at 12:20:38PM +0000, Daniel P. Berrange wrote:
> As is, if the client connects to a TLS enabled NBD server and then
> immediately sends NBD_OPT_EXPORT_NAME, it is not possible for us
> to send back NBD_REP_ERR_TLS_REQD as the spec requires that the
> server close the connection :-(  For this reason I have made the
> qemu NBD client always send NBD_OPT_LIST as the first thing it
> does, so that we can see the NBD_REP_ERR_TLS_REQD response.

Why not have it send NBD_OPT_STARTTLS as the first message if you want
to do TLS? That way, either the server doesn't support it because too
old (and you get NBD_REP_ERR_UNSUP) or configuration (and you get
NBD_REP_ERR_POLICY), or it does and you're in TLS.

Did I miss something?

-- 
It is easy to love a country that is famous for chocolate and beer

  -- Barack Obama, speaking in Brussels, Belgium, 2014-03-26

^ permalink raw reply	[flat|nested] 23+ messages in thread

* Re: [Qemu-devel] [PATCH 00/15] Implement TLS support to QEMU NBD server & client
  2015-12-02 12:56 ` Wouter Verhelst
@ 2015-12-02 13:37   ` Daniel P. Berrange
  2015-12-02 13:45     ` Wouter Verhelst
  0 siblings, 1 reply; 23+ messages in thread
From: Daniel P. Berrange @ 2015-12-02 13:37 UTC (permalink / raw)
  To: Wouter Verhelst; +Cc: Paolo Bonzini, qemu-devel, qemu-block

On Wed, Dec 02, 2015 at 01:56:30PM +0100, Wouter Verhelst wrote:
> Hi Daniel,
> 
> Something occurred to me earlier today:
> 
> On Fri, Nov 27, 2015 at 12:20:38PM +0000, Daniel P. Berrange wrote:
> > As is, if the client connects to a TLS enabled NBD server and then
> > immediately sends NBD_OPT_EXPORT_NAME, it is not possible for us
> > to send back NBD_REP_ERR_TLS_REQD as the spec requires that the
> > server close the connection :-(  For this reason I have made the
> > qemu NBD client always send NBD_OPT_LIST as the first thing it
> > does, so that we can see the NBD_REP_ERR_TLS_REQD response.
> 
> Why not have it send NBD_OPT_STARTTLS as the first message if you want
> to do TLS? That way, either the server doesn't support it because too
> old (and you get NBD_REP_ERR_UNSUP) or configuration (and you get
> NBD_REP_ERR_POLICY), or it does and you're in TLS.
> 
> Did I miss something?

Yes, if the client wants TLS, it will send NBD_OPT_STARTTLS as the
first thing it does.

My comment above was refering to the case of a client not wanting
TLS which is connecting to a server that does want TLS. In that
case sending NBD_OPT_LIST is what we need to do in order to get
a suitable error from the server about requirement for TLS.

Regards,
Daniel
-- 
|: 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 :|

^ permalink raw reply	[flat|nested] 23+ messages in thread

* Re: [Qemu-devel] [PATCH 00/15] Implement TLS support to QEMU NBD server & client
  2015-12-02 13:37   ` Daniel P. Berrange
@ 2015-12-02 13:45     ` Wouter Verhelst
  0 siblings, 0 replies; 23+ messages in thread
From: Wouter Verhelst @ 2015-12-02 13:45 UTC (permalink / raw)
  To: Daniel P. Berrange; +Cc: Paolo Bonzini, qemu-devel, qemu-block

On Wed, Dec 02, 2015 at 01:37:08PM +0000, Daniel P. Berrange wrote:
> On Wed, Dec 02, 2015 at 01:56:30PM +0100, Wouter Verhelst wrote:
> > Hi Daniel,
> > 
> > Something occurred to me earlier today:
> > 
> > On Fri, Nov 27, 2015 at 12:20:38PM +0000, Daniel P. Berrange wrote:
> > > As is, if the client connects to a TLS enabled NBD server and then
> > > immediately sends NBD_OPT_EXPORT_NAME, it is not possible for us
> > > to send back NBD_REP_ERR_TLS_REQD as the spec requires that the
> > > server close the connection :-(  For this reason I have made the
> > > qemu NBD client always send NBD_OPT_LIST as the first thing it
> > > does, so that we can see the NBD_REP_ERR_TLS_REQD response.
> > 
> > Why not have it send NBD_OPT_STARTTLS as the first message if you want
> > to do TLS? That way, either the server doesn't support it because too
> > old (and you get NBD_REP_ERR_UNSUP) or configuration (and you get
> > NBD_REP_ERR_POLICY), or it does and you're in TLS.
> > 
> > Did I miss something?
> 
> Yes, if the client wants TLS, it will send NBD_OPT_STARTTLS as the
> first thing it does.
> 
> My comment above was refering to the case of a client not wanting
> TLS which is connecting to a server that does want TLS. In that
> case sending NBD_OPT_LIST is what we need to do in order to get
> a suitable error from the server about requirement for TLS.

Right, of course. Yes, that makes sense. Ignore what I said :-)

-- 
It is easy to love a country that is famous for chocolate and beer

  -- Barack Obama, speaking in Brussels, Belgium, 2014-03-26

^ permalink raw reply	[flat|nested] 23+ messages in thread

* Re: [Qemu-devel] [PATCH 00/15] Implement TLS support to QEMU NBD server & client
  2015-11-27 14:06 ` [Qemu-devel] [PATCH 00/15] Implement TLS support to QEMU NBD server & client Wouter Verhelst
@ 2015-12-03 14:53   ` Wouter Verhelst
  0 siblings, 0 replies; 23+ messages in thread
From: Wouter Verhelst @ 2015-12-03 14:53 UTC (permalink / raw)
  To: Daniel P. Berrange; +Cc: nbd-general, Paolo Bonzini, qemu-devel, qemu-block

Hi all,

On Fri, Nov 27, 2015 at 03:06:51PM +0100, Wouter Verhelst wrote:
> I have been thinking of adding a message NBD_OPT_SELECT_EXPORT to
> replace NBD_OPT_EXPORT_NAME, which would select an export but not end
> negotiation. That would also require another message to end negotiation
> and move on to the transmission phase, obviously.

I've formalized this now, and removed the PEEK_EXPORT extension (which, as you
say, can't be used safely).

> The negotation would then look something like:
> 
> NBD_OPT_SELECT_EXPORT
>  <- NBD_REP_ACK

This has become an NBD_REP_SERVER instead, with (64-bit) export size and
(16-bit) per-export flags appended (rather than yet another flags field).

(the name is also NBD_OPT_SELECT rather than NBD_OPT_SELECT_EXPORT; details)

NBD_OPT_SELECT is 6, NBD_OPT_GO is 7, NBD_REP_ERR_UNKNOWN is 2^31+6.

For more details, see the proto.md document.

Thoughts are welcome.

-- 
It is easy to love a country that is famous for chocolate and beer

  -- Barack Obama, speaking in Brussels, Belgium, 2014-03-26

^ permalink raw reply	[flat|nested] 23+ messages in thread

end of thread, other threads:[~2015-12-03 14:53 UTC | newest]

Thread overview: 23+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2015-11-27 12:20 [Qemu-devel] [PATCH 00/15] Implement TLS support to QEMU NBD server & client Daniel P. Berrange
2015-11-27 12:20 ` [Qemu-devel] [PATCH 01/15] qom: add user_creatable_add & user_creatable_del methods Daniel P. Berrange
2015-11-27 12:20 ` [Qemu-devel] [PATCH 02/15] qemu-nbd: add support for --object command line arg Daniel P. Berrange
2015-11-27 12:20 ` [Qemu-devel] [PATCH 03/15] nbd: convert block client to use I/O channels for connection setup Daniel P. Berrange
2015-11-27 12:20 ` [Qemu-devel] [PATCH 04/15] nbd: convert qemu-nbd server " Daniel P. Berrange
2015-11-27 12:20 ` [Qemu-devel] [PATCH 05/15] nbd: convert blockdev NBD " Daniel P. Berrange
2015-11-27 12:20 ` [Qemu-devel] [PATCH 06/15] nbd: convert to using I/O channels for actual socket I/O Daniel P. Berrange
2015-11-27 12:20 ` [Qemu-devel] [PATCH 07/15] nbd: invert client logic for negotiating protocol version Daniel P. Berrange
2015-11-27 12:20 ` [Qemu-devel] [PATCH 08/15] nbd: make server compliant with fixed newstyle spec Daniel P. Berrange
2015-11-27 12:20 ` [Qemu-devel] [PATCH 09/15] nbd: make client request fixed new style if advertized Daniel P. Berrange
2015-11-27 12:20 ` [Qemu-devel] [PATCH 10/15] nbd: allow setting of an export name for qemu-nbd server Daniel P. Berrange
2015-11-27 12:20 ` [Qemu-devel] [PATCH 11/15] nbd: pick first exported volume if no export name is requested Daniel P. Berrange
2015-11-27 12:20 ` [Qemu-devel] [PATCH 12/15] nbd: implement TLS support in the protocol negotiation Daniel P. Berrange
2015-11-28 10:28   ` Wouter Verhelst
2015-12-02 10:45     ` Daniel P. Berrange
2015-11-27 12:20 ` [Qemu-devel] [PATCH 13/15] nbd: enable use of TLS with NBD block driver Daniel P. Berrange
2015-11-27 12:20 ` [Qemu-devel] [PATCH 14/15] nbd: enable use of TLS with qemu-nbd server Daniel P. Berrange
2015-11-27 12:20 ` [Qemu-devel] [PATCH 15/15] nbd: enable use of TLS with nbd-server-start command Daniel P. Berrange
2015-11-27 14:06 ` [Qemu-devel] [PATCH 00/15] Implement TLS support to QEMU NBD server & client Wouter Verhelst
2015-12-03 14:53   ` Wouter Verhelst
2015-12-02 12:56 ` Wouter Verhelst
2015-12-02 13:37   ` Daniel P. Berrange
2015-12-02 13:45     ` Wouter Verhelst

This is an external index of several public inboxes,
see mirroring instructions on how to clone and mirror
all data and code used by this external index.