Blame SOURCES/kvm-nbd-client-Add-nbd_receive_export_list.patch

383d26
From 2304c1c3b634b52dcca0a42c4986a04fc2b89369 Mon Sep 17 00:00:00 2001
383d26
From: John Snow <jsnow@redhat.com>
383d26
Date: Wed, 27 Mar 2019 17:22:51 +0100
383d26
Subject: [PATCH 113/163] nbd/client: Add nbd_receive_export_list()
383d26
383d26
RH-Author: John Snow <jsnow@redhat.com>
383d26
Message-id: <20190327172308.31077-39-jsnow@redhat.com>
383d26
Patchwork-id: 85212
383d26
O-Subject: [RHEL-7.7 qemu-kvm-rhev PATCH 38/55] nbd/client: Add nbd_receive_export_list()
383d26
Bugzilla: 1691009
383d26
RH-Acked-by: Stefan Hajnoczi <stefanha@redhat.com>
383d26
RH-Acked-by: Max Reitz <mreitz@redhat.com>
383d26
RH-Acked-by: Miroslav Rezanina <mrezanin@redhat.com>
383d26
383d26
From: Eric Blake <eblake@redhat.com>
383d26
383d26
We want to be able to detect whether a given qemu NBD server is
383d26
exposing the right export(s) and dirty bitmaps, at least for
383d26
regression testing.  We could use 'nbd-client -l' from the upstream
383d26
NBD project to list exports, but it's annoying to rely on
383d26
out-of-tree binaries; furthermore, nbd-client doesn't necessarily
383d26
know about all of the qemu NBD extensions.  Thus, we plan on adding
383d26
a new mode to qemu-nbd that merely sniffs all possible information
383d26
from the server during handshake phase, then disconnects and dumps
383d26
the information.
383d26
383d26
This patch adds the low-level client code for grabbing the list
383d26
of exports.  It benefits from the recent refactoring patches, in
383d26
order to share as much code as possible when it comes to doing
383d26
validation of server replies.  The resulting information is stored
383d26
in an array of NBDExportInfo which has been expanded to any
383d26
description string, along with a convenience function for freeing
383d26
the list.
383d26
383d26
Note: a malicious server could exhaust memory of a client by feeding
383d26
an unending loop of exports; perhaps we should place a limit on how
383d26
many we are willing to receive. But note that a server could
383d26
reasonably be serving an export for every file in a large directory,
383d26
where an arbitrary limit in the client means we can't list anything
383d26
from such a server; the same happens if we just run until the client
383d26
fails to malloc() and thus dies by an abort(), where the limit is
383d26
no longer arbitrary but determined by available memory.  Since the
383d26
client is already planning on being short-lived, it's hard to call
383d26
this a denial of service attack that would starve off other uses,
383d26
so it does not appear to be a security issue.
383d26
383d26
Signed-off-by: Eric Blake <eblake@redhat.com>
383d26
Reviewed-by: Richard W.M. Jones <rjones@redhat.com>
383d26
Message-Id: <20190117193658.16413-18-eblake@redhat.com>
383d26
Reviewed-by: Vladimir Sementsov-Ogievskiy <vsementsov@virtuozzo.com>
383d26
(cherry picked from commit d21a2d3451d7f1defea5104ce28938f228fab0d4)
383d26
Signed-off-by: John Snow <jsnow@redhat.com>
383d26
Signed-off-by: Miroslav Rezanina <mrezanin@redhat.com>
383d26
---
383d26
 include/block/nbd.h |  15 +++++-
383d26
 nbd/client.c        | 132 +++++++++++++++++++++++++++++++++++++++++++++++++++-
383d26
 2 files changed, 143 insertions(+), 4 deletions(-)
383d26
383d26
diff --git a/include/block/nbd.h b/include/block/nbd.h
383d26
index be19aac..19332b4 100644
383d26
--- a/include/block/nbd.h
383d26
+++ b/include/block/nbd.h
383d26
@@ -1,5 +1,5 @@
383d26
 /*
383d26
- *  Copyright (C) 2016-2017 Red Hat, Inc.
383d26
+ *  Copyright (C) 2016-2019 Red Hat, Inc.
383d26
  *  Copyright (C) 2005  Anthony Liguori <anthony@codemonkey.ws>
383d26
  *
383d26
  *  Network Block Device
383d26
@@ -262,6 +262,9 @@ struct NBDExportInfo {
383d26
     /* Set by client before nbd_receive_negotiate() */
383d26
     bool request_sizes;
383d26
     char *x_dirty_bitmap;
383d26
+
383d26
+    /* Set by client before nbd_receive_negotiate(), or by server results
383d26
+     * during nbd_receive_export_list() */
383d26
     char *name; /* must be non-NULL */
383d26
 
383d26
     /* In-out fields, set by client before nbd_receive_negotiate() and
383d26
@@ -269,7 +272,8 @@ struct NBDExportInfo {
383d26
     bool structured_reply;
383d26
     bool base_allocation; /* base:allocation context for NBD_CMD_BLOCK_STATUS */
383d26
 
383d26
-    /* Set by server results during nbd_receive_negotiate() */
383d26
+    /* Set by server results during nbd_receive_negotiate() and
383d26
+     * nbd_receive_export_list() */
383d26
     uint64_t size;
383d26
     uint16_t flags;
383d26
     uint32_t min_block;
383d26
@@ -277,12 +281,19 @@ struct NBDExportInfo {
383d26
     uint32_t max_block;
383d26
 
383d26
     uint32_t context_id;
383d26
+
383d26
+    /* Set by server results during nbd_receive_export_list() */
383d26
+    char *description;
383d26
 };
383d26
 typedef struct NBDExportInfo NBDExportInfo;
383d26
 
383d26
 int nbd_receive_negotiate(QIOChannel *ioc, QCryptoTLSCreds *tlscreds,
383d26
                           const char *hostname, QIOChannel **outioc,
383d26
                           NBDExportInfo *info, Error **errp);
383d26
+void nbd_free_export_list(NBDExportInfo *info, int count);
383d26
+int nbd_receive_export_list(QIOChannel *ioc, QCryptoTLSCreds *tlscreds,
383d26
+                            const char *hostname, NBDExportInfo **info,
383d26
+                            Error **errp);
383d26
 int nbd_init(int fd, QIOChannelSocket *sioc, NBDExportInfo *info,
383d26
              Error **errp);
383d26
 int nbd_send_request(QIOChannel *ioc, NBDRequest *request);
383d26
diff --git a/nbd/client.c b/nbd/client.c
383d26
index fa1657a..8a32169 100644
383d26
--- a/nbd/client.c
383d26
+++ b/nbd/client.c
383d26
@@ -836,7 +836,9 @@ static int nbd_start_negotiate(QIOChannel *ioc, QCryptoTLSCreds *tlscreds,
383d26
 
383d26
     trace_nbd_start_negotiate(tlscreds, hostname ? hostname : "<null>");
383d26
 
383d26
-    *zeroes = true;
383d26
+    if (zeroes) {
383d26
+        *zeroes = true;
383d26
+    }
383d26
     if (outioc) {
383d26
         *outioc = NULL;
383d26
     }
383d26
@@ -880,7 +882,9 @@ static int nbd_start_negotiate(QIOChannel *ioc, QCryptoTLSCreds *tlscreds,
383d26
             clientflags |= NBD_FLAG_C_FIXED_NEWSTYLE;
383d26
         }
383d26
         if (globalflags & NBD_FLAG_NO_ZEROES) {
383d26
-            *zeroes = false;
383d26
+            if (zeroes) {
383d26
+                *zeroes = false;
383d26
+            }
383d26
             clientflags |= NBD_FLAG_C_NO_ZEROES;
383d26
         }
383d26
         /* client requested flags */
383d26
@@ -1059,6 +1063,130 @@ int nbd_receive_negotiate(QIOChannel *ioc, QCryptoTLSCreds *tlscreds,
383d26
     return 0;
383d26
 }
383d26
 
383d26
+/* Clean up result of nbd_receive_export_list */
383d26
+void nbd_free_export_list(NBDExportInfo *info, int count)
383d26
+{
383d26
+    int i;
383d26
+
383d26
+    if (!info) {
383d26
+        return;
383d26
+    }
383d26
+
383d26
+    for (i = 0; i < count; i++) {
383d26
+        g_free(info[i].name);
383d26
+        g_free(info[i].description);
383d26
+    }
383d26
+    g_free(info);
383d26
+}
383d26
+
383d26
+/*
383d26
+ * nbd_receive_export_list:
383d26
+ * Query details about a server's exports, then disconnect without
383d26
+ * going into transmission phase. Return a count of the exports listed
383d26
+ * in @info by the server, or -1 on error. Caller must free @info using
383d26
+ * nbd_free_export_list().
383d26
+ */
383d26
+int nbd_receive_export_list(QIOChannel *ioc, QCryptoTLSCreds *tlscreds,
383d26
+                            const char *hostname, NBDExportInfo **info,
383d26
+                            Error **errp)
383d26
+{
383d26
+    int result;
383d26
+    int count = 0;
383d26
+    int i;
383d26
+    int rc;
383d26
+    int ret = -1;
383d26
+    NBDExportInfo *array = NULL;
383d26
+    QIOChannel *sioc = NULL;
383d26
+
383d26
+    *info = NULL;
383d26
+    result = nbd_start_negotiate(ioc, tlscreds, hostname, &sioc, true, NULL,
383d26
+                                 errp);
383d26
+    if (tlscreds && sioc) {
383d26
+        ioc = sioc;
383d26
+    }
383d26
+
383d26
+    switch (result) {
383d26
+    case 2:
383d26
+    case 3:
383d26
+        /* newstyle - use NBD_OPT_LIST to populate array, then try
383d26
+         * NBD_OPT_INFO on each array member. If structured replies
383d26
+         * are enabled, also try NBD_OPT_LIST_META_CONTEXT. */
383d26
+        if (nbd_send_option_request(ioc, NBD_OPT_LIST, 0, NULL, errp) < 0) {
383d26
+            goto out;
383d26
+        }
383d26
+        while (1) {
383d26
+            char *name;
383d26
+            char *desc;
383d26
+
383d26
+            rc = nbd_receive_list(ioc, &name, &desc, errp);
383d26
+            if (rc < 0) {
383d26
+                goto out;
383d26
+            } else if (rc == 0) {
383d26
+                break;
383d26
+            }
383d26
+            array = g_renew(NBDExportInfo, array, ++count);
383d26
+            memset(&array[count - 1], 0, sizeof(*array));
383d26
+            array[count - 1].name = name;
383d26
+            array[count - 1].description = desc;
383d26
+            array[count - 1].structured_reply = result == 3;
383d26
+        }
383d26
+
383d26
+        for (i = 0; i < count; i++) {
383d26
+            array[i].request_sizes = true;
383d26
+            rc = nbd_opt_info_or_go(ioc, NBD_OPT_INFO, &array[i], errp);
383d26
+            if (rc < 0) {
383d26
+                goto out;
383d26
+            } else if (rc == 0) {
383d26
+                /*
383d26
+                 * Pointless to try rest of loop. If OPT_INFO doesn't work,
383d26
+                 * it's unlikely that meta contexts work either
383d26
+                 */
383d26
+                break;
383d26
+            }
383d26
+
383d26
+            /* TODO: Grab meta contexts */
383d26
+        }
383d26
+
383d26
+        /* Send NBD_OPT_ABORT as a courtesy before hanging up */
383d26
+        nbd_send_opt_abort(ioc);
383d26
+        break;
383d26
+    case 1: /* newstyle, but limited to EXPORT_NAME */
383d26
+        error_setg(errp, "Server does not support export lists");
383d26
+        /* We can't even send NBD_OPT_ABORT, so merely hang up */
383d26
+        goto out;
383d26
+    case 0: /* oldstyle, parse length and flags */
383d26
+        array = g_new0(NBDExportInfo, 1);
383d26
+        array->name = g_strdup("");
383d26
+        count = 1;
383d26
+
383d26
+        if (nbd_negotiate_finish_oldstyle(ioc, array, errp) < 0) {
383d26
+            goto out;
383d26
+        }
383d26
+
383d26
+        /* Send NBD_CMD_DISC as a courtesy to the server, but ignore all
383d26
+         * errors now that we have the information we wanted. */
383d26
+        if (nbd_drop(ioc, 124, NULL) == 0) {
383d26
+            NBDRequest request = { .type = NBD_CMD_DISC };
383d26
+
383d26
+            nbd_send_request(ioc, &request);
383d26
+        }
383d26
+        break;
383d26
+    default:
383d26
+        goto out;
383d26
+    }
383d26
+
383d26
+    *info = array;
383d26
+    array = NULL;
383d26
+    ret = count;
383d26
+
383d26
+ out:
383d26
+    qio_channel_shutdown(ioc, QIO_CHANNEL_SHUTDOWN_BOTH, NULL);
383d26
+    qio_channel_close(ioc, NULL);
383d26
+    object_unref(OBJECT(sioc));
383d26
+    nbd_free_export_list(array, count);
383d26
+    return ret;
383d26
+}
383d26
+
383d26
 #ifdef __linux__
383d26
 int nbd_init(int fd, QIOChannelSocket *sioc, NBDExportInfo *info,
383d26
              Error **errp)
383d26
-- 
383d26
1.8.3.1
383d26