2008-07-03 15:41:03 +02:00
|
|
|
/*
|
2018-01-11 00:08:20 +01:00
|
|
|
* Copyright (C) 2016-2018 Red Hat, Inc.
|
2008-05-27 23:13:40 +02:00
|
|
|
* Copyright (C) 2005 Anthony Liguori <anthony@codemonkey.ws>
|
|
|
|
*
|
2016-01-14 09:41:02 +01:00
|
|
|
* Network Block Device Server Side
|
2008-05-27 23:13:40 +02:00
|
|
|
*
|
|
|
|
* This program is free software; you can redistribute it and/or modify
|
|
|
|
* it under the terms of the GNU General Public License as published by
|
|
|
|
* the Free Software Foundation; under version 2 of the License.
|
|
|
|
*
|
|
|
|
* This program is distributed in the hope that it will be useful,
|
|
|
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
|
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
|
|
* GNU General Public License for more details.
|
|
|
|
*
|
|
|
|
* You should have received a copy of the GNU General Public License
|
2009-07-16 22:47:01 +02:00
|
|
|
* along with this program; if not, see <http://www.gnu.org/licenses/>.
|
2008-07-03 15:41:03 +02:00
|
|
|
*/
|
2008-05-27 23:13:40 +02:00
|
|
|
|
2016-01-29 18:50:05 +01:00
|
|
|
#include "qemu/osdep.h"
|
include/qemu/osdep.h: Don't include qapi/error.h
Commit 57cb38b included qapi/error.h into qemu/osdep.h to get the
Error typedef. Since then, we've moved to include qemu/osdep.h
everywhere. Its file comment explains: "To avoid getting into
possible circular include dependencies, this file should not include
any other QEMU headers, with the exceptions of config-host.h,
compiler.h, os-posix.h and os-win32.h, all of which are doing a
similar job to this file and are under similar constraints."
qapi/error.h doesn't do a similar job, and it doesn't adhere to
similar constraints: it includes qapi-types.h. That's in excess of
100KiB of crap most .c files don't actually need.
Add the typedef to qemu/typedefs.h, and include that instead of
qapi/error.h. Include qapi/error.h in .c files that need it and don't
get it now. Include qapi-types.h in qom/object.h for uint16List.
Update scripts/clean-includes accordingly. Update it further to match
reality: replace config.h by config-target.h, add sysemu/os-posix.h,
sysemu/os-win32.h. Update the list of includes in the qemu/osdep.h
comment quoted above similarly.
This reduces the number of objects depending on qapi/error.h from "all
of them" to less than a third. Unfortunately, the number depending on
qapi-types.h shrinks only a little. More work is needed for that one.
Signed-off-by: Markus Armbruster <armbru@redhat.com>
[Fix compilation without the spice devel packages. - Paolo]
Signed-off-by: Paolo Bonzini <pbonzini@redhat.com>
2016-03-14 09:01:28 +01:00
|
|
|
#include "qapi/error.h"
|
2017-07-07 17:29:18 +02:00
|
|
|
#include "trace.h"
|
2016-01-14 09:41:02 +01:00
|
|
|
#include "nbd-internal.h"
|
qemu-nbd: only send a limited number of errno codes on the wire
Right now, NBD includes potentially platform-specific error values in
the wire protocol.
Luckily, most common error values are more or less universal: in
particular, of all errno values <= 34 (up to ERANGE), they are all the
same on supported platforms except for 11 (which is EAGAIN on Windows and
Linux, but EDEADLK on Darwin and the *BSDs). So, in order to guarantee
some portability, only keep a handful of possible error codes and squash
everything else to EINVAL.
This patch defines a limited set of errno values that are valid for the
NBD protocol, and specifies recommendations for what error to return
in specific corner cases. The set of errno values is roughly based on
the errors listed in the read(2) and write(2) man pages, with some
exceptions:
- ENOMEM is added for servers that implement copy-on-write or other
formats that require dynamic allocation.
- EDQUOT is not part of the universal set of errors; it can be changed
to ENOSPC on the wire format.
- EFBIG is part of the universal set of errors, but it is also changed
to ENOSPC because it is pretty similar to ENOSPC or EDQUOT.
Incoming values will in general match system errno values, but not
on the Hurd which has different errno values (they have a "subsystem
code" equal to 0x10 in bits 24-31). The Hurd is probably not something
to which QEMU has been ported, but still do the right thing and
reverse-map the NBD errno values to the system errno values.
The corresponding patch to the NBD protocol description can be found at
http://article.gmane.org/gmane.linux.drivers.nbd.general/3154.
Signed-off-by: Paolo Bonzini <pbonzini@redhat.com>
2015-05-07 17:25:10 +02:00
|
|
|
|
2018-03-12 16:21:21 +01:00
|
|
|
#define NBD_META_ID_BASE_ALLOCATION 0
|
|
|
|
|
qemu-nbd: only send a limited number of errno codes on the wire
Right now, NBD includes potentially platform-specific error values in
the wire protocol.
Luckily, most common error values are more or less universal: in
particular, of all errno values <= 34 (up to ERANGE), they are all the
same on supported platforms except for 11 (which is EAGAIN on Windows and
Linux, but EDEADLK on Darwin and the *BSDs). So, in order to guarantee
some portability, only keep a handful of possible error codes and squash
everything else to EINVAL.
This patch defines a limited set of errno values that are valid for the
NBD protocol, and specifies recommendations for what error to return
in specific corner cases. The set of errno values is roughly based on
the errors listed in the read(2) and write(2) man pages, with some
exceptions:
- ENOMEM is added for servers that implement copy-on-write or other
formats that require dynamic allocation.
- EDQUOT is not part of the universal set of errors; it can be changed
to ENOSPC on the wire format.
- EFBIG is part of the universal set of errors, but it is also changed
to ENOSPC because it is pretty similar to ENOSPC or EDQUOT.
Incoming values will in general match system errno values, but not
on the Hurd which has different errno values (they have a "subsystem
code" equal to 0x10 in bits 24-31). The Hurd is probably not something
to which QEMU has been ported, but still do the right thing and
reverse-map the NBD errno values to the system errno values.
The corresponding patch to the NBD protocol description can be found at
http://article.gmane.org/gmane.linux.drivers.nbd.general/3154.
Signed-off-by: Paolo Bonzini <pbonzini@redhat.com>
2015-05-07 17:25:10 +02:00
|
|
|
static int system_errno_to_nbd_errno(int err)
|
|
|
|
{
|
|
|
|
switch (err) {
|
|
|
|
case 0:
|
|
|
|
return NBD_SUCCESS;
|
|
|
|
case EPERM:
|
2016-04-06 05:35:02 +02:00
|
|
|
case EROFS:
|
qemu-nbd: only send a limited number of errno codes on the wire
Right now, NBD includes potentially platform-specific error values in
the wire protocol.
Luckily, most common error values are more or less universal: in
particular, of all errno values <= 34 (up to ERANGE), they are all the
same on supported platforms except for 11 (which is EAGAIN on Windows and
Linux, but EDEADLK on Darwin and the *BSDs). So, in order to guarantee
some portability, only keep a handful of possible error codes and squash
everything else to EINVAL.
This patch defines a limited set of errno values that are valid for the
NBD protocol, and specifies recommendations for what error to return
in specific corner cases. The set of errno values is roughly based on
the errors listed in the read(2) and write(2) man pages, with some
exceptions:
- ENOMEM is added for servers that implement copy-on-write or other
formats that require dynamic allocation.
- EDQUOT is not part of the universal set of errors; it can be changed
to ENOSPC on the wire format.
- EFBIG is part of the universal set of errors, but it is also changed
to ENOSPC because it is pretty similar to ENOSPC or EDQUOT.
Incoming values will in general match system errno values, but not
on the Hurd which has different errno values (they have a "subsystem
code" equal to 0x10 in bits 24-31). The Hurd is probably not something
to which QEMU has been ported, but still do the right thing and
reverse-map the NBD errno values to the system errno values.
The corresponding patch to the NBD protocol description can be found at
http://article.gmane.org/gmane.linux.drivers.nbd.general/3154.
Signed-off-by: Paolo Bonzini <pbonzini@redhat.com>
2015-05-07 17:25:10 +02:00
|
|
|
return NBD_EPERM;
|
|
|
|
case EIO:
|
|
|
|
return NBD_EIO;
|
|
|
|
case ENOMEM:
|
|
|
|
return NBD_ENOMEM;
|
|
|
|
#ifdef EDQUOT
|
|
|
|
case EDQUOT:
|
|
|
|
#endif
|
|
|
|
case EFBIG:
|
|
|
|
case ENOSPC:
|
|
|
|
return NBD_ENOSPC;
|
2017-10-27 12:40:28 +02:00
|
|
|
case EOVERFLOW:
|
|
|
|
return NBD_EOVERFLOW;
|
2016-10-14 20:33:16 +02:00
|
|
|
case ESHUTDOWN:
|
|
|
|
return NBD_ESHUTDOWN;
|
qemu-nbd: only send a limited number of errno codes on the wire
Right now, NBD includes potentially platform-specific error values in
the wire protocol.
Luckily, most common error values are more or less universal: in
particular, of all errno values <= 34 (up to ERANGE), they are all the
same on supported platforms except for 11 (which is EAGAIN on Windows and
Linux, but EDEADLK on Darwin and the *BSDs). So, in order to guarantee
some portability, only keep a handful of possible error codes and squash
everything else to EINVAL.
This patch defines a limited set of errno values that are valid for the
NBD protocol, and specifies recommendations for what error to return
in specific corner cases. The set of errno values is roughly based on
the errors listed in the read(2) and write(2) man pages, with some
exceptions:
- ENOMEM is added for servers that implement copy-on-write or other
formats that require dynamic allocation.
- EDQUOT is not part of the universal set of errors; it can be changed
to ENOSPC on the wire format.
- EFBIG is part of the universal set of errors, but it is also changed
to ENOSPC because it is pretty similar to ENOSPC or EDQUOT.
Incoming values will in general match system errno values, but not
on the Hurd which has different errno values (they have a "subsystem
code" equal to 0x10 in bits 24-31). The Hurd is probably not something
to which QEMU has been ported, but still do the right thing and
reverse-map the NBD errno values to the system errno values.
The corresponding patch to the NBD protocol description can be found at
http://article.gmane.org/gmane.linux.drivers.nbd.general/3154.
Signed-off-by: Paolo Bonzini <pbonzini@redhat.com>
2015-05-07 17:25:10 +02:00
|
|
|
case EINVAL:
|
|
|
|
default:
|
|
|
|
return NBD_EINVAL;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2012-08-22 15:30:31 +02:00
|
|
|
/* Definitions for opaque data types */
|
|
|
|
|
2016-10-14 20:33:05 +02:00
|
|
|
typedef struct NBDRequestData NBDRequestData;
|
2012-08-22 15:30:31 +02:00
|
|
|
|
2016-10-14 20:33:05 +02:00
|
|
|
struct NBDRequestData {
|
|
|
|
QSIMPLEQ_ENTRY(NBDRequestData) entry;
|
2012-08-22 15:30:31 +02:00
|
|
|
NBDClient *client;
|
|
|
|
uint8_t *data;
|
2016-05-12 00:39:37 +02:00
|
|
|
bool complete;
|
2012-08-22 15:30:31 +02:00
|
|
|
};
|
|
|
|
|
|
|
|
struct NBDExport {
|
2012-09-18 13:26:25 +02:00
|
|
|
int refcount;
|
2012-09-18 13:59:03 +02:00
|
|
|
void (*close)(NBDExport *exp);
|
|
|
|
|
2014-11-18 12:21:18 +01:00
|
|
|
BlockBackend *blk;
|
2012-08-22 15:59:23 +02:00
|
|
|
char *name;
|
2016-10-14 20:33:03 +02:00
|
|
|
char *description;
|
2012-08-22 15:30:31 +02:00
|
|
|
off_t dev_offset;
|
|
|
|
off_t size;
|
2016-07-21 21:34:46 +02:00
|
|
|
uint16_t nbdflags;
|
2012-09-18 13:58:25 +02:00
|
|
|
QTAILQ_HEAD(, NBDClient) clients;
|
2012-08-22 15:59:23 +02:00
|
|
|
QTAILQ_ENTRY(NBDExport) next;
|
2014-06-20 21:57:32 +02:00
|
|
|
|
|
|
|
AioContext *ctx;
|
2016-01-29 16:36:06 +01:00
|
|
|
|
2016-07-06 11:22:39 +02:00
|
|
|
BlockBackend *eject_notifier_blk;
|
2016-01-29 16:36:06 +01:00
|
|
|
Notifier eject_notifier;
|
2012-08-22 15:30:31 +02:00
|
|
|
};
|
|
|
|
|
2012-08-22 15:59:23 +02:00
|
|
|
static QTAILQ_HEAD(, NBDExport) exports = QTAILQ_HEAD_INITIALIZER(exports);
|
|
|
|
|
2018-03-12 16:21:21 +01:00
|
|
|
/* NBDExportMetaContexts represents a list of contexts to be exported,
|
|
|
|
* as selected by NBD_OPT_SET_META_CONTEXT. Also used for
|
|
|
|
* NBD_OPT_LIST_META_CONTEXT. */
|
|
|
|
typedef struct NBDExportMetaContexts {
|
|
|
|
char export_name[NBD_MAX_NAME_SIZE + 1];
|
|
|
|
bool valid; /* means that negotiation of the option finished without
|
|
|
|
errors */
|
|
|
|
bool base_allocation; /* export base:allocation context (block status) */
|
|
|
|
} NBDExportMetaContexts;
|
|
|
|
|
2012-08-22 15:30:31 +02:00
|
|
|
struct NBDClient {
|
|
|
|
int refcount;
|
nbd: Fix regression on resiliency to port scan
Back in qemu 2.5, qemu-nbd was immune to port probes (a transient
server would not quit, regardless of how many probe connections
came and went, until a connection actually negotiated). But we
broke that in commit ee7d7aa when removing the return value to
nbd_client_new(), although that patch also introduced a bug causing
an assertion failure on a client that fails negotiation. We then
made it worse during refactoring in commit 1a6245a (a segfault
before we could even assert); the (masked) assertion was cleaned
up in d3780c2 (still in 2.6), and just recently we finally fixed
the segfault ("nbd: Fully intialize client in case of failed
negotiation"). But that still means that ever since we added
TLS support to qemu-nbd, we have been vulnerable to an ill-timed
port-scan being able to cause a denial of service by taking down
qemu-nbd before a real client has a chance to connect.
Since negotiation is now handled asynchronously via coroutines,
we no longer have a synchronous point of return by re-adding a
return value to nbd_client_new(). So this patch instead wires
things up to pass the negotiation status through the close_fn
callback function.
Simple test across two terminals:
$ qemu-nbd -f raw -p 30001 file
$ nmap 127.0.0.1 -p 30001 && \
qemu-io -c 'r 0 512' -f raw nbd://localhost:30001
Note that this patch does not change what constitutes successful
negotiation (thus, a client must enter transmission phase before
that client can be considered as a reason to terminate the server
when the connection ends). Perhaps we may want to tweak things
in a later patch to also treat a client that uses NBD_OPT_ABORT
as being a 'successful' negotiation (the client correctly talked
the NBD protocol, and informed us it was not going to use our
export after all), but that's a discussion for another day.
Fixes: https://bugzilla.redhat.com/show_bug.cgi?id=1451614
Signed-off-by: Eric Blake <eblake@redhat.com>
Message-Id: <20170608222617.20376-1-eblake@redhat.com>
Signed-off-by: Paolo Bonzini <pbonzini@redhat.com>
2017-06-09 00:26:17 +02:00
|
|
|
void (*close_fn)(NBDClient *client, bool negotiated);
|
2012-08-22 15:30:31 +02:00
|
|
|
|
|
|
|
NBDExport *exp;
|
2016-02-10 19:41:11 +01:00
|
|
|
QCryptoTLSCreds *tlscreds;
|
|
|
|
char *tlsaclname;
|
2016-02-10 19:41:04 +01:00
|
|
|
QIOChannelSocket *sioc; /* The underlying data channel */
|
|
|
|
QIOChannel *ioc; /* The current I/O channel which may differ (eg TLS) */
|
2012-08-22 15:30:31 +02:00
|
|
|
|
|
|
|
Coroutine *recv_coroutine;
|
|
|
|
|
|
|
|
CoMutex send_lock;
|
|
|
|
Coroutine *send_coroutine;
|
|
|
|
|
2012-09-18 13:58:25 +02:00
|
|
|
QTAILQ_ENTRY(NBDClient) next;
|
2012-08-22 15:30:31 +02:00
|
|
|
int nb_requests;
|
2012-08-22 18:45:12 +02:00
|
|
|
bool closing;
|
2017-10-27 12:40:32 +02:00
|
|
|
|
|
|
|
bool structured_reply;
|
2018-03-12 16:21:21 +01:00
|
|
|
NBDExportMetaContexts export_meta;
|
2012-08-22 15:30:31 +02:00
|
|
|
|
2018-01-11 00:08:21 +01:00
|
|
|
uint32_t opt; /* Current option being negotiated */
|
|
|
|
uint32_t optlen; /* remaining length of data in ioc for the option being
|
|
|
|
negotiated now */
|
|
|
|
};
|
2008-05-27 23:13:40 +02:00
|
|
|
|
2017-02-13 14:52:24 +01:00
|
|
|
static void nbd_client_receive_next_request(NBDClient *client);
|
2014-06-20 21:57:32 +02:00
|
|
|
|
2012-08-23 14:57:11 +02:00
|
|
|
/* Basic flow for negotiation
|
2008-05-27 23:13:40 +02:00
|
|
|
|
|
|
|
Server Client
|
|
|
|
Negotiate
|
2012-08-23 14:57:11 +02:00
|
|
|
|
|
|
|
or
|
|
|
|
|
|
|
|
Server Client
|
|
|
|
Negotiate #1
|
|
|
|
Option
|
|
|
|
Negotiate #2
|
|
|
|
|
|
|
|
----
|
|
|
|
|
|
|
|
followed by
|
|
|
|
|
|
|
|
Server Client
|
2008-05-27 23:13:40 +02:00
|
|
|
Request
|
|
|
|
Response
|
|
|
|
Request
|
|
|
|
Response
|
|
|
|
...
|
|
|
|
...
|
|
|
|
Request (type == 2)
|
2012-08-23 14:57:11 +02:00
|
|
|
|
2008-05-27 23:13:40 +02:00
|
|
|
*/
|
|
|
|
|
2018-01-11 00:08:25 +01:00
|
|
|
static inline void set_be_option_rep(NBDOptionReply *rep, uint32_t option,
|
|
|
|
uint32_t type, uint32_t length)
|
|
|
|
{
|
|
|
|
stq_be_p(&rep->magic, NBD_REP_MAGIC);
|
|
|
|
stl_be_p(&rep->option, option);
|
|
|
|
stl_be_p(&rep->type, type);
|
|
|
|
stl_be_p(&rep->length, length);
|
|
|
|
}
|
|
|
|
|
2016-10-14 20:33:08 +02:00
|
|
|
/* Send a reply header, including length, but no payload.
|
|
|
|
* Return -errno on error, 0 on success. */
|
2018-01-11 00:08:21 +01:00
|
|
|
static int nbd_negotiate_send_rep_len(NBDClient *client, uint32_t type,
|
|
|
|
uint32_t len, Error **errp)
|
2012-08-23 14:57:11 +02:00
|
|
|
{
|
2018-01-11 00:08:25 +01:00
|
|
|
NBDOptionReply rep;
|
2012-08-23 14:57:11 +02:00
|
|
|
|
2018-01-11 00:08:25 +01:00
|
|
|
trace_nbd_negotiate_send_rep_len(client->opt, nbd_opt_lookup(client->opt),
|
2017-07-07 22:30:43 +02:00
|
|
|
type, nbd_rep_lookup(type), len);
|
2016-02-10 19:41:11 +01:00
|
|
|
|
2017-07-07 22:30:46 +02:00
|
|
|
assert(len < NBD_MAX_BUFFER_SIZE);
|
2017-07-07 17:29:11 +02:00
|
|
|
|
2018-01-11 00:08:25 +01:00
|
|
|
set_be_option_rep(&rep, client->opt, type, len);
|
|
|
|
return nbd_write(client->ioc, &rep, sizeof(rep), errp);
|
2014-06-07 02:32:31 +02:00
|
|
|
}
|
2012-08-23 14:57:11 +02:00
|
|
|
|
2016-10-14 20:33:08 +02:00
|
|
|
/* Send a reply header with default 0 length.
|
|
|
|
* Return -errno on error, 0 on success. */
|
2018-01-11 00:08:21 +01:00
|
|
|
static int nbd_negotiate_send_rep(NBDClient *client, uint32_t type,
|
2017-07-07 17:29:11 +02:00
|
|
|
Error **errp)
|
2016-10-14 20:33:08 +02:00
|
|
|
{
|
2018-01-11 00:08:21 +01:00
|
|
|
return nbd_negotiate_send_rep_len(client, type, 0, errp);
|
2016-10-14 20:33:08 +02:00
|
|
|
}
|
|
|
|
|
2016-10-14 20:33:09 +02:00
|
|
|
/* Send an error reply.
|
|
|
|
* Return -errno on error, 0 on success. */
|
2018-01-11 00:08:23 +01:00
|
|
|
static int GCC_FMT_ATTR(4, 0)
|
|
|
|
nbd_negotiate_send_rep_verr(NBDClient *client, uint32_t type,
|
|
|
|
Error **errp, const char *fmt, va_list va)
|
2016-10-14 20:33:09 +02:00
|
|
|
{
|
|
|
|
char *msg;
|
|
|
|
int ret;
|
|
|
|
size_t len;
|
|
|
|
|
|
|
|
msg = g_strdup_vprintf(fmt, va);
|
|
|
|
len = strlen(msg);
|
|
|
|
assert(len < 4096);
|
2017-07-07 17:29:18 +02:00
|
|
|
trace_nbd_negotiate_send_rep_err(msg);
|
2018-01-11 00:08:21 +01:00
|
|
|
ret = nbd_negotiate_send_rep_len(client, type, len, errp);
|
2016-10-14 20:33:09 +02:00
|
|
|
if (ret < 0) {
|
|
|
|
goto out;
|
|
|
|
}
|
2018-01-11 00:08:21 +01:00
|
|
|
if (nbd_write(client->ioc, msg, len, errp) < 0) {
|
2017-07-07 17:29:11 +02:00
|
|
|
error_prepend(errp, "write failed (error message): ");
|
2016-10-14 20:33:09 +02:00
|
|
|
ret = -EIO;
|
|
|
|
} else {
|
|
|
|
ret = 0;
|
|
|
|
}
|
2017-07-07 17:29:11 +02:00
|
|
|
|
2016-10-14 20:33:09 +02:00
|
|
|
out:
|
|
|
|
g_free(msg);
|
|
|
|
return ret;
|
|
|
|
}
|
|
|
|
|
2018-01-11 00:08:23 +01:00
|
|
|
/* Send an error reply.
|
|
|
|
* Return -errno on error, 0 on success. */
|
|
|
|
static int GCC_FMT_ATTR(4, 5)
|
|
|
|
nbd_negotiate_send_rep_err(NBDClient *client, uint32_t type,
|
|
|
|
Error **errp, const char *fmt, ...)
|
|
|
|
{
|
|
|
|
va_list va;
|
|
|
|
int ret;
|
|
|
|
|
|
|
|
va_start(va, fmt);
|
|
|
|
ret = nbd_negotiate_send_rep_verr(client, type, errp, fmt, va);
|
|
|
|
va_end(va);
|
|
|
|
return ret;
|
|
|
|
}
|
|
|
|
|
2018-01-11 00:08:24 +01:00
|
|
|
/* Drop remainder of the current option, and send a reply with the
|
|
|
|
* given error type and message. Return -errno on read or write
|
|
|
|
* failure; or 0 if connection is still live. */
|
2018-03-12 16:21:19 +01:00
|
|
|
static int GCC_FMT_ATTR(4, 0)
|
|
|
|
nbd_opt_vdrop(NBDClient *client, uint32_t type, Error **errp,
|
|
|
|
const char *fmt, va_list va)
|
2018-01-11 00:08:24 +01:00
|
|
|
{
|
|
|
|
int ret = nbd_drop(client->ioc, client->optlen, errp);
|
|
|
|
|
|
|
|
client->optlen = 0;
|
|
|
|
if (!ret) {
|
|
|
|
ret = nbd_negotiate_send_rep_verr(client, type, errp, fmt, va);
|
|
|
|
}
|
|
|
|
return ret;
|
|
|
|
}
|
|
|
|
|
2018-03-12 16:21:19 +01:00
|
|
|
static int GCC_FMT_ATTR(4, 5)
|
|
|
|
nbd_opt_drop(NBDClient *client, uint32_t type, Error **errp,
|
|
|
|
const char *fmt, ...)
|
|
|
|
{
|
|
|
|
int ret;
|
|
|
|
va_list va;
|
|
|
|
|
|
|
|
va_start(va, fmt);
|
|
|
|
ret = nbd_opt_vdrop(client, type, errp, fmt, va);
|
|
|
|
va_end(va);
|
|
|
|
|
|
|
|
return ret;
|
|
|
|
}
|
|
|
|
|
|
|
|
static int GCC_FMT_ATTR(3, 4)
|
|
|
|
nbd_opt_invalid(NBDClient *client, Error **errp, const char *fmt, ...)
|
|
|
|
{
|
|
|
|
int ret;
|
|
|
|
va_list va;
|
|
|
|
|
|
|
|
va_start(va, fmt);
|
|
|
|
ret = nbd_opt_vdrop(client, NBD_REP_ERR_INVALID, errp, fmt, va);
|
|
|
|
va_end(va);
|
|
|
|
|
|
|
|
return ret;
|
|
|
|
}
|
|
|
|
|
2018-01-11 00:08:24 +01:00
|
|
|
/* Read size bytes from the unparsed payload of the current option.
|
|
|
|
* Return -errno on I/O error, 0 if option was completely handled by
|
|
|
|
* sending a reply about inconsistent lengths, or 1 on success. */
|
|
|
|
static int nbd_opt_read(NBDClient *client, void *buffer, size_t size,
|
|
|
|
Error **errp)
|
|
|
|
{
|
|
|
|
if (size > client->optlen) {
|
2018-03-12 16:21:19 +01:00
|
|
|
return nbd_opt_invalid(client, errp,
|
|
|
|
"Inconsistent lengths in option %s",
|
|
|
|
nbd_opt_lookup(client->opt));
|
2018-01-11 00:08:24 +01:00
|
|
|
}
|
|
|
|
client->optlen -= size;
|
|
|
|
return qio_channel_read_all(client->ioc, buffer, size, errp) < 0 ? -EIO : 1;
|
|
|
|
}
|
|
|
|
|
2018-03-12 16:21:21 +01:00
|
|
|
/* Drop size bytes from the unparsed payload of the current option.
|
|
|
|
* Return -errno on I/O error, 0 if option was completely handled by
|
|
|
|
* sending a reply about inconsistent lengths, or 1 on success. */
|
|
|
|
static int nbd_opt_skip(NBDClient *client, size_t size, Error **errp)
|
|
|
|
{
|
|
|
|
if (size > client->optlen) {
|
|
|
|
return nbd_opt_invalid(client, errp,
|
|
|
|
"Inconsistent lengths in option %s",
|
|
|
|
nbd_opt_lookup(client->opt));
|
|
|
|
}
|
|
|
|
client->optlen -= size;
|
|
|
|
return nbd_drop(client->ioc, size, errp) < 0 ? -EIO : 1;
|
|
|
|
}
|
|
|
|
|
2018-03-12 16:21:20 +01:00
|
|
|
/* nbd_opt_read_name
|
|
|
|
*
|
|
|
|
* Read a string with the format:
|
|
|
|
* uint32_t len (<= NBD_MAX_NAME_SIZE)
|
|
|
|
* len bytes string (not 0-terminated)
|
|
|
|
*
|
|
|
|
* @name should be enough to store NBD_MAX_NAME_SIZE+1.
|
|
|
|
* If @length is non-null, it will be set to the actual string length.
|
|
|
|
*
|
|
|
|
* Return -errno on I/O error, 0 if option was completely handled by
|
|
|
|
* sending a reply about inconsistent lengths, or 1 on success.
|
|
|
|
*/
|
|
|
|
static int nbd_opt_read_name(NBDClient *client, char *name, uint32_t *length,
|
|
|
|
Error **errp)
|
|
|
|
{
|
|
|
|
int ret;
|
|
|
|
uint32_t len;
|
|
|
|
|
|
|
|
ret = nbd_opt_read(client, &len, sizeof(len), errp);
|
|
|
|
if (ret <= 0) {
|
|
|
|
return ret;
|
|
|
|
}
|
|
|
|
cpu_to_be32s(&len);
|
|
|
|
|
|
|
|
if (len > NBD_MAX_NAME_SIZE) {
|
|
|
|
return nbd_opt_invalid(client, errp,
|
|
|
|
"Invalid name length: %" PRIu32, len);
|
|
|
|
}
|
|
|
|
|
|
|
|
ret = nbd_opt_read(client, name, len, errp);
|
|
|
|
if (ret <= 0) {
|
|
|
|
return ret;
|
|
|
|
}
|
|
|
|
name[len] = '\0';
|
|
|
|
|
|
|
|
if (length) {
|
|
|
|
*length = len;
|
|
|
|
}
|
|
|
|
|
|
|
|
return 1;
|
|
|
|
}
|
|
|
|
|
2016-10-14 20:33:08 +02:00
|
|
|
/* Send a single NBD_REP_SERVER reply to NBD_OPT_LIST, including payload.
|
|
|
|
* Return -errno on error, 0 on success. */
|
2018-01-11 00:08:21 +01:00
|
|
|
static int nbd_negotiate_send_rep_list(NBDClient *client, NBDExport *exp,
|
2017-07-07 17:29:11 +02:00
|
|
|
Error **errp)
|
2014-06-07 02:32:32 +02:00
|
|
|
{
|
2016-10-14 20:33:03 +02:00
|
|
|
size_t name_len, desc_len;
|
2016-10-14 20:33:08 +02:00
|
|
|
uint32_t len;
|
2016-10-14 20:33:03 +02:00
|
|
|
const char *name = exp->name ? exp->name : "";
|
|
|
|
const char *desc = exp->description ? exp->description : "";
|
2018-01-11 00:08:21 +01:00
|
|
|
QIOChannel *ioc = client->ioc;
|
2017-06-02 17:01:49 +02:00
|
|
|
int ret;
|
2014-06-07 02:32:32 +02:00
|
|
|
|
2017-07-07 17:29:18 +02:00
|
|
|
trace_nbd_negotiate_send_rep_list(name, desc);
|
2016-10-14 20:33:03 +02:00
|
|
|
name_len = strlen(name);
|
|
|
|
desc_len = strlen(desc);
|
2016-10-14 20:33:08 +02:00
|
|
|
len = name_len + desc_len + sizeof(len);
|
2018-01-11 00:08:21 +01:00
|
|
|
ret = nbd_negotiate_send_rep_len(client, NBD_REP_SERVER, len, errp);
|
2017-06-02 17:01:49 +02:00
|
|
|
if (ret < 0) {
|
|
|
|
return ret;
|
2014-06-07 02:32:32 +02:00
|
|
|
}
|
2016-10-14 20:33:08 +02:00
|
|
|
|
2014-06-07 02:32:32 +02:00
|
|
|
len = cpu_to_be32(name_len);
|
2017-07-07 17:29:11 +02:00
|
|
|
if (nbd_write(ioc, &len, sizeof(len), errp) < 0) {
|
|
|
|
error_prepend(errp, "write failed (name length): ");
|
2016-10-14 20:33:03 +02:00
|
|
|
return -EINVAL;
|
|
|
|
}
|
2017-07-07 17:29:11 +02:00
|
|
|
|
|
|
|
if (nbd_write(ioc, name, name_len, errp) < 0) {
|
|
|
|
error_prepend(errp, "write failed (name buffer): ");
|
2014-06-07 02:32:32 +02:00
|
|
|
return -EINVAL;
|
|
|
|
}
|
2017-07-07 17:29:11 +02:00
|
|
|
|
|
|
|
if (nbd_write(ioc, desc, desc_len, errp) < 0) {
|
|
|
|
error_prepend(errp, "write failed (description buffer): ");
|
2014-06-07 02:32:32 +02:00
|
|
|
return -EINVAL;
|
|
|
|
}
|
2017-07-07 17:29:11 +02:00
|
|
|
|
2014-06-07 02:32:32 +02:00
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
2016-10-14 20:33:08 +02:00
|
|
|
/* Process the NBD_OPT_LIST command, with a potential series of replies.
|
|
|
|
* Return -errno on error, 0 on success. */
|
2017-10-27 12:40:31 +02:00
|
|
|
static int nbd_negotiate_handle_list(NBDClient *client, Error **errp)
|
2014-06-07 02:32:32 +02:00
|
|
|
{
|
|
|
|
NBDExport *exp;
|
2018-01-11 00:08:21 +01:00
|
|
|
assert(client->opt == NBD_OPT_LIST);
|
2014-06-07 02:32:32 +02:00
|
|
|
|
|
|
|
/* For each export, send a NBD_REP_SERVER reply. */
|
|
|
|
QTAILQ_FOREACH(exp, &exports, next) {
|
2018-01-11 00:08:21 +01:00
|
|
|
if (nbd_negotiate_send_rep_list(client, exp, errp)) {
|
2014-06-07 02:32:32 +02:00
|
|
|
return -EINVAL;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
/* Finish with a NBD_REP_ACK. */
|
2018-01-11 00:08:21 +01:00
|
|
|
return nbd_negotiate_send_rep(client, NBD_REP_ACK, errp);
|
2014-06-07 02:32:32 +02:00
|
|
|
}
|
|
|
|
|
2018-03-12 16:21:21 +01:00
|
|
|
static void nbd_check_meta_export_name(NBDClient *client)
|
|
|
|
{
|
|
|
|
client->export_meta.valid &= !strcmp(client->exp->name,
|
|
|
|
client->export_meta.export_name);
|
|
|
|
}
|
|
|
|
|
2017-07-07 22:30:46 +02:00
|
|
|
/* Send a reply to NBD_OPT_EXPORT_NAME.
|
|
|
|
* Return -errno on error, 0 on success. */
|
2018-01-11 00:08:21 +01:00
|
|
|
static int nbd_negotiate_handle_export_name(NBDClient *client,
|
2017-07-07 22:30:45 +02:00
|
|
|
uint16_t myflags, bool no_zeroes,
|
2017-07-07 17:29:11 +02:00
|
|
|
Error **errp)
|
2014-06-07 02:32:31 +02:00
|
|
|
{
|
2016-05-12 00:39:44 +02:00
|
|
|
char name[NBD_MAX_NAME_SIZE + 1];
|
2017-07-17 21:26:35 +02:00
|
|
|
char buf[NBD_REPLY_EXPORT_NAME_SIZE] = "";
|
2017-07-07 22:30:45 +02:00
|
|
|
size_t len;
|
|
|
|
int ret;
|
2012-08-23 14:57:11 +02:00
|
|
|
|
2014-06-07 02:32:31 +02:00
|
|
|
/* Client sends:
|
|
|
|
[20 .. xx] export name (length bytes)
|
2017-07-17 21:26:35 +02:00
|
|
|
Server replies:
|
|
|
|
[ 0 .. 7] size
|
|
|
|
[ 8 .. 9] export flags
|
|
|
|
[10 .. 133] reserved (0) [unless no_zeroes]
|
2014-06-07 02:32:31 +02:00
|
|
|
*/
|
2017-07-07 17:29:18 +02:00
|
|
|
trace_nbd_negotiate_handle_export_name();
|
2018-01-11 00:08:21 +01:00
|
|
|
if (client->optlen >= sizeof(name)) {
|
2017-07-07 17:29:11 +02:00
|
|
|
error_setg(errp, "Bad length received");
|
2017-06-02 17:01:48 +02:00
|
|
|
return -EINVAL;
|
2012-08-23 14:57:11 +02:00
|
|
|
}
|
2018-01-11 00:08:21 +01:00
|
|
|
if (nbd_read(client->ioc, name, client->optlen, errp) < 0) {
|
2017-07-07 17:29:11 +02:00
|
|
|
error_prepend(errp, "read failed: ");
|
2018-01-11 00:08:22 +01:00
|
|
|
return -EIO;
|
2012-08-23 14:57:11 +02:00
|
|
|
}
|
2018-01-11 00:08:21 +01:00
|
|
|
name[client->optlen] = '\0';
|
|
|
|
client->optlen = 0;
|
2012-08-23 14:57:11 +02:00
|
|
|
|
2017-07-07 17:29:18 +02:00
|
|
|
trace_nbd_negotiate_handle_export_name_request(name);
|
2016-02-10 19:41:09 +01:00
|
|
|
|
2012-08-23 14:57:11 +02:00
|
|
|
client->exp = nbd_export_find(name);
|
|
|
|
if (!client->exp) {
|
2017-07-07 17:29:11 +02:00
|
|
|
error_setg(errp, "export not found");
|
2017-06-02 17:01:48 +02:00
|
|
|
return -EINVAL;
|
2012-08-23 14:57:11 +02:00
|
|
|
}
|
|
|
|
|
2017-07-07 22:30:45 +02:00
|
|
|
trace_nbd_negotiate_new_style_size_flags(client->exp->size,
|
|
|
|
client->exp->nbdflags | myflags);
|
|
|
|
stq_be_p(buf, client->exp->size);
|
|
|
|
stw_be_p(buf + 8, client->exp->nbdflags | myflags);
|
|
|
|
len = no_zeroes ? 10 : sizeof(buf);
|
|
|
|
ret = nbd_write(client->ioc, buf, len, errp);
|
|
|
|
if (ret < 0) {
|
|
|
|
error_prepend(errp, "write failed: ");
|
|
|
|
return ret;
|
|
|
|
}
|
|
|
|
|
2012-08-23 14:57:11 +02:00
|
|
|
QTAILQ_INSERT_TAIL(&client->exp->clients, client, next);
|
|
|
|
nbd_export_get(client->exp);
|
2018-03-12 16:21:21 +01:00
|
|
|
nbd_check_meta_export_name(client);
|
2017-06-02 17:01:48 +02:00
|
|
|
|
|
|
|
return 0;
|
2012-08-23 14:57:11 +02:00
|
|
|
}
|
|
|
|
|
2017-07-07 22:30:46 +02:00
|
|
|
/* Send a single NBD_REP_INFO, with a buffer @buf of @length bytes.
|
|
|
|
* The buffer does NOT include the info type prefix.
|
|
|
|
* Return -errno on error, 0 if ready to send more. */
|
2018-01-11 00:08:21 +01:00
|
|
|
static int nbd_negotiate_send_info(NBDClient *client,
|
2017-07-07 22:30:46 +02:00
|
|
|
uint16_t info, uint32_t length, void *buf,
|
|
|
|
Error **errp)
|
|
|
|
{
|
|
|
|
int rc;
|
|
|
|
|
|
|
|
trace_nbd_negotiate_send_info(info, nbd_info_lookup(info), length);
|
2018-01-11 00:08:21 +01:00
|
|
|
rc = nbd_negotiate_send_rep_len(client, NBD_REP_INFO,
|
2017-07-07 22:30:46 +02:00
|
|
|
sizeof(info) + length, errp);
|
|
|
|
if (rc < 0) {
|
|
|
|
return rc;
|
|
|
|
}
|
|
|
|
cpu_to_be16s(&info);
|
|
|
|
if (nbd_write(client->ioc, &info, sizeof(info), errp) < 0) {
|
|
|
|
return -EIO;
|
|
|
|
}
|
|
|
|
if (nbd_write(client->ioc, buf, length, errp) < 0) {
|
|
|
|
return -EIO;
|
|
|
|
}
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
2018-01-11 00:08:20 +01:00
|
|
|
/* nbd_reject_length: Handle any unexpected payload.
|
|
|
|
* @fatal requests that we quit talking to the client, even if we are able
|
|
|
|
* to successfully send an error reply.
|
|
|
|
* Return:
|
|
|
|
* -errno transmission error occurred or @fatal was requested, errp is set
|
|
|
|
* 0 error message successfully sent to client, errp is not set
|
|
|
|
*/
|
2018-01-11 00:08:21 +01:00
|
|
|
static int nbd_reject_length(NBDClient *client, bool fatal, Error **errp)
|
2018-01-11 00:08:20 +01:00
|
|
|
{
|
|
|
|
int ret;
|
|
|
|
|
2018-01-11 00:08:21 +01:00
|
|
|
assert(client->optlen);
|
2018-03-12 16:21:19 +01:00
|
|
|
ret = nbd_opt_invalid(client, errp, "option '%s' has unexpected length",
|
|
|
|
nbd_opt_lookup(client->opt));
|
2018-01-11 00:08:20 +01:00
|
|
|
if (fatal && !ret) {
|
2018-01-11 00:08:24 +01:00
|
|
|
error_setg(errp, "option '%s' has unexpected length",
|
2018-01-11 00:08:21 +01:00
|
|
|
nbd_opt_lookup(client->opt));
|
2018-01-11 00:08:20 +01:00
|
|
|
return -EINVAL;
|
|
|
|
}
|
|
|
|
return ret;
|
|
|
|
}
|
|
|
|
|
2017-07-07 22:30:46 +02:00
|
|
|
/* Handle NBD_OPT_INFO and NBD_OPT_GO.
|
|
|
|
* Return -errno on error, 0 if ready for next option, and 1 to move
|
|
|
|
* into transmission phase. */
|
2018-01-11 00:08:21 +01:00
|
|
|
static int nbd_negotiate_handle_info(NBDClient *client, uint16_t myflags,
|
2017-07-07 22:30:46 +02:00
|
|
|
Error **errp)
|
|
|
|
{
|
|
|
|
int rc;
|
|
|
|
char name[NBD_MAX_NAME_SIZE + 1];
|
|
|
|
NBDExport *exp;
|
|
|
|
uint16_t requests;
|
|
|
|
uint16_t request;
|
|
|
|
uint32_t namelen;
|
|
|
|
bool sendname = false;
|
nbd: Implement NBD_INFO_BLOCK_SIZE on server
The upstream NBD Protocol has defined a new extension to allow
the server to advertise block sizes to the client, as well as
a way for the client to inform the server that it intends to
obey block sizes.
Thanks to a recent fix (commit df7b97ff), our real minimum
transfer size is always 1 (the block layer takes care of
read-modify-write on our behalf), but we're still more efficient
if we advertise 512 when the client supports it, as follows:
- OPT_INFO, but no NBD_INFO_BLOCK_SIZE: advertise 512, then
fail with NBD_REP_ERR_BLOCK_SIZE_REQD; client is free to try
something else since we don't disconnect
- OPT_INFO with NBD_INFO_BLOCK_SIZE: advertise 512
- OPT_GO, but no NBD_INFO_BLOCK_SIZE: advertise 1
- OPT_GO with NBD_INFO_BLOCK_SIZE: advertise 512
We can also advertise the optimum block size (presumably the
cluster size, when exporting a qcow2 file), and our absolute
maximum transfer size of 32M, to help newer clients avoid
EINVAL failures or abrupt disconnects on oversize requests.
We do not reject clients for using the older NBD_OPT_EXPORT_NAME;
we are no worse off for those clients than we used to be.
Signed-off-by: Eric Blake <eblake@redhat.com>
Message-Id: <20170707203049.534-9-eblake@redhat.com>
Signed-off-by: Paolo Bonzini <pbonzini@redhat.com>
2017-07-07 22:30:48 +02:00
|
|
|
bool blocksize = false;
|
|
|
|
uint32_t sizes[3];
|
2017-07-07 22:30:46 +02:00
|
|
|
char buf[sizeof(uint64_t) + sizeof(uint16_t)];
|
|
|
|
|
|
|
|
/* Client sends:
|
|
|
|
4 bytes: L, name length (can be 0)
|
|
|
|
L bytes: export name
|
|
|
|
2 bytes: N, number of requests (can be 0)
|
|
|
|
N * 2 bytes: N requests
|
|
|
|
*/
|
2018-03-12 16:21:20 +01:00
|
|
|
rc = nbd_opt_read_name(client, name, &namelen, errp);
|
2018-01-11 00:08:24 +01:00
|
|
|
if (rc <= 0) {
|
|
|
|
return rc;
|
2017-07-07 22:30:46 +02:00
|
|
|
}
|
|
|
|
trace_nbd_negotiate_handle_export_name_request(name);
|
|
|
|
|
2018-01-11 00:08:24 +01:00
|
|
|
rc = nbd_opt_read(client, &requests, sizeof(requests), errp);
|
|
|
|
if (rc <= 0) {
|
|
|
|
return rc;
|
2017-07-07 22:30:46 +02:00
|
|
|
}
|
|
|
|
be16_to_cpus(&requests);
|
|
|
|
trace_nbd_negotiate_handle_info_requests(requests);
|
|
|
|
while (requests--) {
|
2018-01-11 00:08:24 +01:00
|
|
|
rc = nbd_opt_read(client, &request, sizeof(request), errp);
|
|
|
|
if (rc <= 0) {
|
|
|
|
return rc;
|
2017-07-07 22:30:46 +02:00
|
|
|
}
|
|
|
|
be16_to_cpus(&request);
|
|
|
|
trace_nbd_negotiate_handle_info_request(request,
|
|
|
|
nbd_info_lookup(request));
|
nbd: Implement NBD_INFO_BLOCK_SIZE on server
The upstream NBD Protocol has defined a new extension to allow
the server to advertise block sizes to the client, as well as
a way for the client to inform the server that it intends to
obey block sizes.
Thanks to a recent fix (commit df7b97ff), our real minimum
transfer size is always 1 (the block layer takes care of
read-modify-write on our behalf), but we're still more efficient
if we advertise 512 when the client supports it, as follows:
- OPT_INFO, but no NBD_INFO_BLOCK_SIZE: advertise 512, then
fail with NBD_REP_ERR_BLOCK_SIZE_REQD; client is free to try
something else since we don't disconnect
- OPT_INFO with NBD_INFO_BLOCK_SIZE: advertise 512
- OPT_GO, but no NBD_INFO_BLOCK_SIZE: advertise 1
- OPT_GO with NBD_INFO_BLOCK_SIZE: advertise 512
We can also advertise the optimum block size (presumably the
cluster size, when exporting a qcow2 file), and our absolute
maximum transfer size of 32M, to help newer clients avoid
EINVAL failures or abrupt disconnects on oversize requests.
We do not reject clients for using the older NBD_OPT_EXPORT_NAME;
we are no worse off for those clients than we used to be.
Signed-off-by: Eric Blake <eblake@redhat.com>
Message-Id: <20170707203049.534-9-eblake@redhat.com>
Signed-off-by: Paolo Bonzini <pbonzini@redhat.com>
2017-07-07 22:30:48 +02:00
|
|
|
/* We care about NBD_INFO_NAME and NBD_INFO_BLOCK_SIZE;
|
|
|
|
* everything else is either a request we don't know or
|
|
|
|
* something we send regardless of request */
|
|
|
|
switch (request) {
|
|
|
|
case NBD_INFO_NAME:
|
2017-07-07 22:30:46 +02:00
|
|
|
sendname = true;
|
nbd: Implement NBD_INFO_BLOCK_SIZE on server
The upstream NBD Protocol has defined a new extension to allow
the server to advertise block sizes to the client, as well as
a way for the client to inform the server that it intends to
obey block sizes.
Thanks to a recent fix (commit df7b97ff), our real minimum
transfer size is always 1 (the block layer takes care of
read-modify-write on our behalf), but we're still more efficient
if we advertise 512 when the client supports it, as follows:
- OPT_INFO, but no NBD_INFO_BLOCK_SIZE: advertise 512, then
fail with NBD_REP_ERR_BLOCK_SIZE_REQD; client is free to try
something else since we don't disconnect
- OPT_INFO with NBD_INFO_BLOCK_SIZE: advertise 512
- OPT_GO, but no NBD_INFO_BLOCK_SIZE: advertise 1
- OPT_GO with NBD_INFO_BLOCK_SIZE: advertise 512
We can also advertise the optimum block size (presumably the
cluster size, when exporting a qcow2 file), and our absolute
maximum transfer size of 32M, to help newer clients avoid
EINVAL failures or abrupt disconnects on oversize requests.
We do not reject clients for using the older NBD_OPT_EXPORT_NAME;
we are no worse off for those clients than we used to be.
Signed-off-by: Eric Blake <eblake@redhat.com>
Message-Id: <20170707203049.534-9-eblake@redhat.com>
Signed-off-by: Paolo Bonzini <pbonzini@redhat.com>
2017-07-07 22:30:48 +02:00
|
|
|
break;
|
|
|
|
case NBD_INFO_BLOCK_SIZE:
|
|
|
|
blocksize = true;
|
|
|
|
break;
|
2017-07-07 22:30:46 +02:00
|
|
|
}
|
|
|
|
}
|
2018-01-11 00:08:24 +01:00
|
|
|
if (client->optlen) {
|
|
|
|
return nbd_reject_length(client, false, errp);
|
|
|
|
}
|
2017-07-07 22:30:46 +02:00
|
|
|
|
|
|
|
exp = nbd_export_find(name);
|
|
|
|
if (!exp) {
|
2018-01-11 00:08:21 +01:00
|
|
|
return nbd_negotiate_send_rep_err(client, NBD_REP_ERR_UNKNOWN,
|
|
|
|
errp, "export '%s' not present",
|
2017-07-07 22:30:46 +02:00
|
|
|
name);
|
|
|
|
}
|
|
|
|
|
|
|
|
/* Don't bother sending NBD_INFO_NAME unless client requested it */
|
|
|
|
if (sendname) {
|
2018-01-11 00:08:21 +01:00
|
|
|
rc = nbd_negotiate_send_info(client, NBD_INFO_NAME, namelen, name,
|
2017-07-07 22:30:46 +02:00
|
|
|
errp);
|
|
|
|
if (rc < 0) {
|
|
|
|
return rc;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
/* Send NBD_INFO_DESCRIPTION only if available, regardless of
|
|
|
|
* client request */
|
|
|
|
if (exp->description) {
|
|
|
|
size_t len = strlen(exp->description);
|
|
|
|
|
2018-01-11 00:08:21 +01:00
|
|
|
rc = nbd_negotiate_send_info(client, NBD_INFO_DESCRIPTION,
|
2017-07-07 22:30:46 +02:00
|
|
|
len, exp->description, errp);
|
|
|
|
if (rc < 0) {
|
|
|
|
return rc;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
nbd: Implement NBD_INFO_BLOCK_SIZE on server
The upstream NBD Protocol has defined a new extension to allow
the server to advertise block sizes to the client, as well as
a way for the client to inform the server that it intends to
obey block sizes.
Thanks to a recent fix (commit df7b97ff), our real minimum
transfer size is always 1 (the block layer takes care of
read-modify-write on our behalf), but we're still more efficient
if we advertise 512 when the client supports it, as follows:
- OPT_INFO, but no NBD_INFO_BLOCK_SIZE: advertise 512, then
fail with NBD_REP_ERR_BLOCK_SIZE_REQD; client is free to try
something else since we don't disconnect
- OPT_INFO with NBD_INFO_BLOCK_SIZE: advertise 512
- OPT_GO, but no NBD_INFO_BLOCK_SIZE: advertise 1
- OPT_GO with NBD_INFO_BLOCK_SIZE: advertise 512
We can also advertise the optimum block size (presumably the
cluster size, when exporting a qcow2 file), and our absolute
maximum transfer size of 32M, to help newer clients avoid
EINVAL failures or abrupt disconnects on oversize requests.
We do not reject clients for using the older NBD_OPT_EXPORT_NAME;
we are no worse off for those clients than we used to be.
Signed-off-by: Eric Blake <eblake@redhat.com>
Message-Id: <20170707203049.534-9-eblake@redhat.com>
Signed-off-by: Paolo Bonzini <pbonzini@redhat.com>
2017-07-07 22:30:48 +02:00
|
|
|
/* Send NBD_INFO_BLOCK_SIZE always, but tweak the minimum size
|
|
|
|
* according to whether the client requested it, and according to
|
|
|
|
* whether this is OPT_INFO or OPT_GO. */
|
|
|
|
/* minimum - 1 for back-compat, or 512 if client is new enough.
|
|
|
|
* TODO: consult blk_bs(blk)->bl.request_alignment? */
|
2018-01-11 00:08:21 +01:00
|
|
|
sizes[0] =
|
|
|
|
(client->opt == NBD_OPT_INFO || blocksize) ? BDRV_SECTOR_SIZE : 1;
|
nbd: Implement NBD_INFO_BLOCK_SIZE on server
The upstream NBD Protocol has defined a new extension to allow
the server to advertise block sizes to the client, as well as
a way for the client to inform the server that it intends to
obey block sizes.
Thanks to a recent fix (commit df7b97ff), our real minimum
transfer size is always 1 (the block layer takes care of
read-modify-write on our behalf), but we're still more efficient
if we advertise 512 when the client supports it, as follows:
- OPT_INFO, but no NBD_INFO_BLOCK_SIZE: advertise 512, then
fail with NBD_REP_ERR_BLOCK_SIZE_REQD; client is free to try
something else since we don't disconnect
- OPT_INFO with NBD_INFO_BLOCK_SIZE: advertise 512
- OPT_GO, but no NBD_INFO_BLOCK_SIZE: advertise 1
- OPT_GO with NBD_INFO_BLOCK_SIZE: advertise 512
We can also advertise the optimum block size (presumably the
cluster size, when exporting a qcow2 file), and our absolute
maximum transfer size of 32M, to help newer clients avoid
EINVAL failures or abrupt disconnects on oversize requests.
We do not reject clients for using the older NBD_OPT_EXPORT_NAME;
we are no worse off for those clients than we used to be.
Signed-off-by: Eric Blake <eblake@redhat.com>
Message-Id: <20170707203049.534-9-eblake@redhat.com>
Signed-off-by: Paolo Bonzini <pbonzini@redhat.com>
2017-07-07 22:30:48 +02:00
|
|
|
/* preferred - Hard-code to 4096 for now.
|
|
|
|
* TODO: is blk_bs(blk)->bl.opt_transfer appropriate? */
|
|
|
|
sizes[1] = 4096;
|
|
|
|
/* maximum - At most 32M, but smaller as appropriate. */
|
|
|
|
sizes[2] = MIN(blk_get_max_transfer(exp->blk), NBD_MAX_BUFFER_SIZE);
|
|
|
|
trace_nbd_negotiate_handle_info_block_size(sizes[0], sizes[1], sizes[2]);
|
|
|
|
cpu_to_be32s(&sizes[0]);
|
|
|
|
cpu_to_be32s(&sizes[1]);
|
|
|
|
cpu_to_be32s(&sizes[2]);
|
2018-01-11 00:08:21 +01:00
|
|
|
rc = nbd_negotiate_send_info(client, NBD_INFO_BLOCK_SIZE,
|
nbd: Implement NBD_INFO_BLOCK_SIZE on server
The upstream NBD Protocol has defined a new extension to allow
the server to advertise block sizes to the client, as well as
a way for the client to inform the server that it intends to
obey block sizes.
Thanks to a recent fix (commit df7b97ff), our real minimum
transfer size is always 1 (the block layer takes care of
read-modify-write on our behalf), but we're still more efficient
if we advertise 512 when the client supports it, as follows:
- OPT_INFO, but no NBD_INFO_BLOCK_SIZE: advertise 512, then
fail with NBD_REP_ERR_BLOCK_SIZE_REQD; client is free to try
something else since we don't disconnect
- OPT_INFO with NBD_INFO_BLOCK_SIZE: advertise 512
- OPT_GO, but no NBD_INFO_BLOCK_SIZE: advertise 1
- OPT_GO with NBD_INFO_BLOCK_SIZE: advertise 512
We can also advertise the optimum block size (presumably the
cluster size, when exporting a qcow2 file), and our absolute
maximum transfer size of 32M, to help newer clients avoid
EINVAL failures or abrupt disconnects on oversize requests.
We do not reject clients for using the older NBD_OPT_EXPORT_NAME;
we are no worse off for those clients than we used to be.
Signed-off-by: Eric Blake <eblake@redhat.com>
Message-Id: <20170707203049.534-9-eblake@redhat.com>
Signed-off-by: Paolo Bonzini <pbonzini@redhat.com>
2017-07-07 22:30:48 +02:00
|
|
|
sizeof(sizes), sizes, errp);
|
|
|
|
if (rc < 0) {
|
|
|
|
return rc;
|
|
|
|
}
|
|
|
|
|
2017-07-07 22:30:46 +02:00
|
|
|
/* Send NBD_INFO_EXPORT always */
|
|
|
|
trace_nbd_negotiate_new_style_size_flags(exp->size,
|
|
|
|
exp->nbdflags | myflags);
|
|
|
|
stq_be_p(buf, exp->size);
|
|
|
|
stw_be_p(buf + 8, exp->nbdflags | myflags);
|
2018-01-11 00:08:21 +01:00
|
|
|
rc = nbd_negotiate_send_info(client, NBD_INFO_EXPORT,
|
2017-07-07 22:30:46 +02:00
|
|
|
sizeof(buf), buf, errp);
|
|
|
|
if (rc < 0) {
|
|
|
|
return rc;
|
|
|
|
}
|
|
|
|
|
nbd: Implement NBD_INFO_BLOCK_SIZE on server
The upstream NBD Protocol has defined a new extension to allow
the server to advertise block sizes to the client, as well as
a way for the client to inform the server that it intends to
obey block sizes.
Thanks to a recent fix (commit df7b97ff), our real minimum
transfer size is always 1 (the block layer takes care of
read-modify-write on our behalf), but we're still more efficient
if we advertise 512 when the client supports it, as follows:
- OPT_INFO, but no NBD_INFO_BLOCK_SIZE: advertise 512, then
fail with NBD_REP_ERR_BLOCK_SIZE_REQD; client is free to try
something else since we don't disconnect
- OPT_INFO with NBD_INFO_BLOCK_SIZE: advertise 512
- OPT_GO, but no NBD_INFO_BLOCK_SIZE: advertise 1
- OPT_GO with NBD_INFO_BLOCK_SIZE: advertise 512
We can also advertise the optimum block size (presumably the
cluster size, when exporting a qcow2 file), and our absolute
maximum transfer size of 32M, to help newer clients avoid
EINVAL failures or abrupt disconnects on oversize requests.
We do not reject clients for using the older NBD_OPT_EXPORT_NAME;
we are no worse off for those clients than we used to be.
Signed-off-by: Eric Blake <eblake@redhat.com>
Message-Id: <20170707203049.534-9-eblake@redhat.com>
Signed-off-by: Paolo Bonzini <pbonzini@redhat.com>
2017-07-07 22:30:48 +02:00
|
|
|
/* If the client is just asking for NBD_OPT_INFO, but forgot to
|
|
|
|
* request block sizes, return an error.
|
|
|
|
* TODO: consult blk_bs(blk)->request_align, and only error if it
|
|
|
|
* is not 1? */
|
2018-01-11 00:08:21 +01:00
|
|
|
if (client->opt == NBD_OPT_INFO && !blocksize) {
|
|
|
|
return nbd_negotiate_send_rep_err(client,
|
|
|
|
NBD_REP_ERR_BLOCK_SIZE_REQD,
|
nbd: Implement NBD_INFO_BLOCK_SIZE on server
The upstream NBD Protocol has defined a new extension to allow
the server to advertise block sizes to the client, as well as
a way for the client to inform the server that it intends to
obey block sizes.
Thanks to a recent fix (commit df7b97ff), our real minimum
transfer size is always 1 (the block layer takes care of
read-modify-write on our behalf), but we're still more efficient
if we advertise 512 when the client supports it, as follows:
- OPT_INFO, but no NBD_INFO_BLOCK_SIZE: advertise 512, then
fail with NBD_REP_ERR_BLOCK_SIZE_REQD; client is free to try
something else since we don't disconnect
- OPT_INFO with NBD_INFO_BLOCK_SIZE: advertise 512
- OPT_GO, but no NBD_INFO_BLOCK_SIZE: advertise 1
- OPT_GO with NBD_INFO_BLOCK_SIZE: advertise 512
We can also advertise the optimum block size (presumably the
cluster size, when exporting a qcow2 file), and our absolute
maximum transfer size of 32M, to help newer clients avoid
EINVAL failures or abrupt disconnects on oversize requests.
We do not reject clients for using the older NBD_OPT_EXPORT_NAME;
we are no worse off for those clients than we used to be.
Signed-off-by: Eric Blake <eblake@redhat.com>
Message-Id: <20170707203049.534-9-eblake@redhat.com>
Signed-off-by: Paolo Bonzini <pbonzini@redhat.com>
2017-07-07 22:30:48 +02:00
|
|
|
errp,
|
|
|
|
"request NBD_INFO_BLOCK_SIZE to "
|
|
|
|
"use this export");
|
|
|
|
}
|
|
|
|
|
2017-07-07 22:30:46 +02:00
|
|
|
/* Final reply */
|
2018-01-11 00:08:21 +01:00
|
|
|
rc = nbd_negotiate_send_rep(client, NBD_REP_ACK, errp);
|
2017-07-07 22:30:46 +02:00
|
|
|
if (rc < 0) {
|
|
|
|
return rc;
|
|
|
|
}
|
|
|
|
|
2018-01-11 00:08:21 +01:00
|
|
|
if (client->opt == NBD_OPT_GO) {
|
2017-07-07 22:30:46 +02:00
|
|
|
client->exp = exp;
|
|
|
|
QTAILQ_INSERT_TAIL(&client->exp->clients, client, next);
|
|
|
|
nbd_export_get(client->exp);
|
2018-03-12 16:21:21 +01:00
|
|
|
nbd_check_meta_export_name(client);
|
2017-07-07 22:30:46 +02:00
|
|
|
rc = 1;
|
|
|
|
}
|
|
|
|
return rc;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2016-10-14 20:33:09 +02:00
|
|
|
/* Handle NBD_OPT_STARTTLS. Return NULL to drop connection, or else the
|
|
|
|
* new channel for all further (now-encrypted) communication. */
|
2016-02-10 19:41:11 +01:00
|
|
|
static QIOChannel *nbd_negotiate_handle_starttls(NBDClient *client,
|
2017-07-07 17:29:11 +02:00
|
|
|
Error **errp)
|
2016-02-10 19:41:11 +01:00
|
|
|
{
|
|
|
|
QIOChannel *ioc;
|
|
|
|
QIOChannelTLS *tioc;
|
|
|
|
struct NBDTLSHandshakeData data = { 0 };
|
|
|
|
|
2018-01-11 00:08:21 +01:00
|
|
|
assert(client->opt == NBD_OPT_STARTTLS);
|
|
|
|
|
2017-07-07 17:29:18 +02:00
|
|
|
trace_nbd_negotiate_handle_starttls();
|
2016-02-10 19:41:11 +01:00
|
|
|
ioc = client->ioc;
|
|
|
|
|
2018-01-11 00:08:21 +01:00
|
|
|
if (nbd_negotiate_send_rep(client, NBD_REP_ACK, errp) < 0) {
|
2016-05-12 00:39:36 +02:00
|
|
|
return NULL;
|
|
|
|
}
|
2016-02-10 19:41:11 +01:00
|
|
|
|
|
|
|
tioc = qio_channel_tls_new_server(ioc,
|
|
|
|
client->tlscreds,
|
|
|
|
client->tlsaclname,
|
2017-07-07 17:29:11 +02:00
|
|
|
errp);
|
2016-02-10 19:41:11 +01:00
|
|
|
if (!tioc) {
|
|
|
|
return NULL;
|
|
|
|
}
|
|
|
|
|
2016-09-30 12:57:14 +02:00
|
|
|
qio_channel_set_name(QIO_CHANNEL(tioc), "nbd-server-tls");
|
2017-07-07 17:29:18 +02:00
|
|
|
trace_nbd_negotiate_handle_starttls_handshake();
|
2016-02-10 19:41:11 +01:00
|
|
|
data.loop = g_main_loop_new(g_main_context_default(), FALSE);
|
|
|
|
qio_channel_tls_handshake(tioc,
|
|
|
|
nbd_tls_handshake,
|
|
|
|
&data,
|
2018-03-05 07:43:24 +01:00
|
|
|
NULL,
|
2016-02-10 19:41:11 +01:00
|
|
|
NULL);
|
|
|
|
|
|
|
|
if (!data.complete) {
|
|
|
|
g_main_loop_run(data.loop);
|
|
|
|
}
|
|
|
|
g_main_loop_unref(data.loop);
|
|
|
|
if (data.error) {
|
|
|
|
object_unref(OBJECT(tioc));
|
2017-07-07 17:29:11 +02:00
|
|
|
error_propagate(errp, data.error);
|
2016-02-10 19:41:11 +01:00
|
|
|
return NULL;
|
|
|
|
}
|
|
|
|
|
|
|
|
return QIO_CHANNEL(tioc);
|
|
|
|
}
|
|
|
|
|
2018-03-12 16:21:21 +01:00
|
|
|
/* nbd_negotiate_send_meta_context
|
|
|
|
*
|
|
|
|
* Send one chunk of reply to NBD_OPT_{LIST,SET}_META_CONTEXT
|
|
|
|
*
|
|
|
|
* For NBD_OPT_LIST_META_CONTEXT @context_id is ignored, 0 is used instead.
|
|
|
|
*/
|
|
|
|
static int nbd_negotiate_send_meta_context(NBDClient *client,
|
|
|
|
const char *context,
|
|
|
|
uint32_t context_id,
|
|
|
|
Error **errp)
|
|
|
|
{
|
|
|
|
NBDOptionReplyMetaContext opt;
|
|
|
|
struct iovec iov[] = {
|
|
|
|
{.iov_base = &opt, .iov_len = sizeof(opt)},
|
|
|
|
{.iov_base = (void *)context, .iov_len = strlen(context)}
|
|
|
|
};
|
|
|
|
|
|
|
|
if (client->opt == NBD_OPT_LIST_META_CONTEXT) {
|
|
|
|
context_id = 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
set_be_option_rep(&opt.h, client->opt, NBD_REP_META_CONTEXT,
|
|
|
|
sizeof(opt) - sizeof(opt.h) + iov[1].iov_len);
|
|
|
|
stl_be_p(&opt.context_id, context_id);
|
|
|
|
|
|
|
|
return qio_channel_writev_all(client->ioc, iov, 2, errp) < 0 ? -EIO : 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* nbd_meta_base_query
|
|
|
|
*
|
|
|
|
* Handle query to 'base' namespace. For now, only base:allocation context is
|
|
|
|
* available in it. 'len' is the amount of text remaining to be read from
|
|
|
|
* the current name, after the 'base:' portion has been stripped.
|
|
|
|
*
|
|
|
|
* Return -errno on I/O error, 0 if option was completely handled by
|
|
|
|
* sending a reply about inconsistent lengths, or 1 on success. */
|
|
|
|
static int nbd_meta_base_query(NBDClient *client, NBDExportMetaContexts *meta,
|
|
|
|
uint32_t len, Error **errp)
|
|
|
|
{
|
|
|
|
int ret;
|
|
|
|
char query[sizeof("allocation") - 1];
|
|
|
|
size_t alen = strlen("allocation");
|
|
|
|
|
|
|
|
if (len == 0) {
|
|
|
|
if (client->opt == NBD_OPT_LIST_META_CONTEXT) {
|
|
|
|
meta->base_allocation = true;
|
|
|
|
}
|
|
|
|
return 1;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (len != alen) {
|
|
|
|
return nbd_opt_skip(client, len, errp);
|
|
|
|
}
|
|
|
|
|
|
|
|
ret = nbd_opt_read(client, query, len, errp);
|
|
|
|
if (ret <= 0) {
|
|
|
|
return ret;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (strncmp(query, "allocation", alen) == 0) {
|
|
|
|
meta->base_allocation = true;
|
|
|
|
}
|
|
|
|
|
|
|
|
return 1;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* nbd_negotiate_meta_query
|
|
|
|
*
|
|
|
|
* Parse namespace name and call corresponding function to parse body of the
|
|
|
|
* query.
|
|
|
|
*
|
|
|
|
* The only supported namespace now is 'base'.
|
|
|
|
*
|
|
|
|
* The function aims not wasting time and memory to read long unknown namespace
|
|
|
|
* names.
|
|
|
|
*
|
|
|
|
* Return -errno on I/O error, 0 if option was completely handled by
|
|
|
|
* sending a reply about inconsistent lengths, or 1 on success. */
|
|
|
|
static int nbd_negotiate_meta_query(NBDClient *client,
|
|
|
|
NBDExportMetaContexts *meta, Error **errp)
|
|
|
|
{
|
|
|
|
int ret;
|
|
|
|
char query[sizeof("base:") - 1];
|
|
|
|
size_t baselen = strlen("base:");
|
|
|
|
uint32_t len;
|
|
|
|
|
|
|
|
ret = nbd_opt_read(client, &len, sizeof(len), errp);
|
|
|
|
if (ret <= 0) {
|
|
|
|
return ret;
|
|
|
|
}
|
|
|
|
cpu_to_be32s(&len);
|
|
|
|
|
|
|
|
/* The only supported namespace for now is 'base'. So query should start
|
|
|
|
* with 'base:'. Otherwise, we can ignore it and skip the remainder. */
|
|
|
|
if (len < baselen) {
|
|
|
|
return nbd_opt_skip(client, len, errp);
|
|
|
|
}
|
|
|
|
|
|
|
|
len -= baselen;
|
|
|
|
ret = nbd_opt_read(client, query, baselen, errp);
|
|
|
|
if (ret <= 0) {
|
|
|
|
return ret;
|
|
|
|
}
|
|
|
|
if (strncmp(query, "base:", baselen) != 0) {
|
|
|
|
return nbd_opt_skip(client, len, errp);
|
|
|
|
}
|
|
|
|
|
|
|
|
return nbd_meta_base_query(client, meta, len, errp);
|
|
|
|
}
|
|
|
|
|
|
|
|
/* nbd_negotiate_meta_queries
|
|
|
|
* Handle NBD_OPT_LIST_META_CONTEXT and NBD_OPT_SET_META_CONTEXT
|
|
|
|
*
|
|
|
|
* Return -errno on I/O error, or 0 if option was completely handled. */
|
|
|
|
static int nbd_negotiate_meta_queries(NBDClient *client,
|
|
|
|
NBDExportMetaContexts *meta, Error **errp)
|
|
|
|
{
|
|
|
|
int ret;
|
|
|
|
NBDExport *exp;
|
|
|
|
NBDExportMetaContexts local_meta;
|
|
|
|
uint32_t nb_queries;
|
|
|
|
int i;
|
|
|
|
|
|
|
|
if (!client->structured_reply) {
|
|
|
|
return nbd_opt_invalid(client, errp,
|
|
|
|
"request option '%s' when structured reply "
|
|
|
|
"is not negotiated",
|
|
|
|
nbd_opt_lookup(client->opt));
|
|
|
|
}
|
|
|
|
|
|
|
|
if (client->opt == NBD_OPT_LIST_META_CONTEXT) {
|
|
|
|
/* Only change the caller's meta on SET. */
|
|
|
|
meta = &local_meta;
|
|
|
|
}
|
|
|
|
|
|
|
|
memset(meta, 0, sizeof(*meta));
|
|
|
|
|
|
|
|
ret = nbd_opt_read_name(client, meta->export_name, NULL, errp);
|
|
|
|
if (ret <= 0) {
|
|
|
|
return ret;
|
|
|
|
}
|
|
|
|
|
|
|
|
exp = nbd_export_find(meta->export_name);
|
|
|
|
if (exp == NULL) {
|
|
|
|
return nbd_opt_drop(client, NBD_REP_ERR_UNKNOWN, errp,
|
|
|
|
"export '%s' not present", meta->export_name);
|
|
|
|
}
|
|
|
|
|
|
|
|
ret = nbd_opt_read(client, &nb_queries, sizeof(nb_queries), errp);
|
|
|
|
if (ret <= 0) {
|
|
|
|
return ret;
|
|
|
|
}
|
|
|
|
cpu_to_be32s(&nb_queries);
|
|
|
|
|
|
|
|
if (client->opt == NBD_OPT_LIST_META_CONTEXT && !nb_queries) {
|
|
|
|
/* enable all known contexts */
|
|
|
|
meta->base_allocation = true;
|
|
|
|
} else {
|
|
|
|
for (i = 0; i < nb_queries; ++i) {
|
|
|
|
ret = nbd_negotiate_meta_query(client, meta, errp);
|
|
|
|
if (ret <= 0) {
|
|
|
|
return ret;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
if (meta->base_allocation) {
|
|
|
|
ret = nbd_negotiate_send_meta_context(client, "base:allocation",
|
|
|
|
NBD_META_ID_BASE_ALLOCATION,
|
|
|
|
errp);
|
|
|
|
if (ret < 0) {
|
|
|
|
return ret;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
ret = nbd_negotiate_send_rep(client, NBD_REP_ACK, errp);
|
|
|
|
if (ret == 0) {
|
|
|
|
meta->valid = true;
|
|
|
|
}
|
|
|
|
|
|
|
|
return ret;
|
|
|
|
}
|
|
|
|
|
2017-07-07 17:29:09 +02:00
|
|
|
/* nbd_negotiate_options
|
2017-07-07 22:30:46 +02:00
|
|
|
* Process all NBD_OPT_* client option commands, during fixed newstyle
|
|
|
|
* negotiation.
|
2017-07-07 17:29:09 +02:00
|
|
|
* Return:
|
2017-07-07 17:29:11 +02:00
|
|
|
* -errno on error, errp is set
|
|
|
|
* 0 on successful negotiation, errp is not set
|
|
|
|
* 1 if client sent NBD_OPT_ABORT, i.e. on valid disconnect,
|
|
|
|
* errp is not set
|
2017-07-07 17:29:09 +02:00
|
|
|
*/
|
2017-07-07 22:30:45 +02:00
|
|
|
static int nbd_negotiate_options(NBDClient *client, uint16_t myflags,
|
|
|
|
Error **errp)
|
2014-06-07 02:32:31 +02:00
|
|
|
{
|
2015-02-25 19:08:31 +01:00
|
|
|
uint32_t flags;
|
2016-02-10 19:41:06 +01:00
|
|
|
bool fixedNewstyle = false;
|
2017-07-07 22:30:45 +02:00
|
|
|
bool no_zeroes = false;
|
2015-02-25 19:08:31 +01:00
|
|
|
|
|
|
|
/* Client sends:
|
|
|
|
[ 0 .. 3] client flags
|
|
|
|
|
2017-07-07 22:30:46 +02:00
|
|
|
Then we loop until NBD_OPT_EXPORT_NAME or NBD_OPT_GO:
|
2015-02-25 19:08:31 +01:00
|
|
|
[ 0 .. 7] NBD_OPTS_MAGIC
|
|
|
|
[ 8 .. 11] NBD option
|
|
|
|
[12 .. 15] Data length
|
|
|
|
... Rest of request
|
|
|
|
|
|
|
|
[ 0 .. 7] NBD_OPTS_MAGIC
|
|
|
|
[ 8 .. 11] Second NBD option
|
|
|
|
[12 .. 15] Data length
|
|
|
|
... Rest of request
|
|
|
|
*/
|
|
|
|
|
2017-07-07 17:29:11 +02:00
|
|
|
if (nbd_read(client->ioc, &flags, sizeof(flags), errp) < 0) {
|
|
|
|
error_prepend(errp, "read failed: ");
|
2015-02-25 19:08:31 +01:00
|
|
|
return -EIO;
|
|
|
|
}
|
|
|
|
be32_to_cpus(&flags);
|
2017-07-07 22:30:44 +02:00
|
|
|
trace_nbd_negotiate_options_flags(flags);
|
2016-02-10 19:41:06 +01:00
|
|
|
if (flags & NBD_FLAG_C_FIXED_NEWSTYLE) {
|
|
|
|
fixedNewstyle = true;
|
|
|
|
flags &= ~NBD_FLAG_C_FIXED_NEWSTYLE;
|
|
|
|
}
|
2016-10-14 20:33:14 +02:00
|
|
|
if (flags & NBD_FLAG_C_NO_ZEROES) {
|
2017-07-07 22:30:45 +02:00
|
|
|
no_zeroes = true;
|
2016-10-14 20:33:14 +02:00
|
|
|
flags &= ~NBD_FLAG_C_NO_ZEROES;
|
|
|
|
}
|
2016-02-10 19:41:06 +01:00
|
|
|
if (flags != 0) {
|
2017-07-07 17:29:11 +02:00
|
|
|
error_setg(errp, "Unknown client flags 0x%" PRIx32 " received", flags);
|
2017-07-07 22:30:44 +02:00
|
|
|
return -EINVAL;
|
2015-02-25 19:08:31 +01:00
|
|
|
}
|
|
|
|
|
2014-06-07 02:32:31 +02:00
|
|
|
while (1) {
|
2015-02-25 19:08:31 +01:00
|
|
|
int ret;
|
2017-07-07 17:29:16 +02:00
|
|
|
uint32_t option, length;
|
2014-06-07 02:32:31 +02:00
|
|
|
uint64_t magic;
|
|
|
|
|
2017-07-07 17:29:11 +02:00
|
|
|
if (nbd_read(client->ioc, &magic, sizeof(magic), errp) < 0) {
|
|
|
|
error_prepend(errp, "read failed: ");
|
2014-06-07 02:32:31 +02:00
|
|
|
return -EINVAL;
|
|
|
|
}
|
2017-07-07 17:29:18 +02:00
|
|
|
magic = be64_to_cpu(magic);
|
|
|
|
trace_nbd_negotiate_options_check_magic(magic);
|
|
|
|
if (magic != NBD_OPTS_MAGIC) {
|
2017-07-07 17:29:11 +02:00
|
|
|
error_setg(errp, "Bad magic received");
|
2014-06-07 02:32:31 +02:00
|
|
|
return -EINVAL;
|
|
|
|
}
|
|
|
|
|
2017-07-07 17:29:16 +02:00
|
|
|
if (nbd_read(client->ioc, &option,
|
|
|
|
sizeof(option), errp) < 0) {
|
2017-07-07 17:29:11 +02:00
|
|
|
error_prepend(errp, "read failed: ");
|
2014-06-07 02:32:31 +02:00
|
|
|
return -EINVAL;
|
|
|
|
}
|
2017-07-07 17:29:16 +02:00
|
|
|
option = be32_to_cpu(option);
|
2018-01-11 00:08:21 +01:00
|
|
|
client->opt = option;
|
2014-06-07 02:32:31 +02:00
|
|
|
|
2017-07-07 17:29:11 +02:00
|
|
|
if (nbd_read(client->ioc, &length, sizeof(length), errp) < 0) {
|
|
|
|
error_prepend(errp, "read failed: ");
|
2014-06-07 02:32:31 +02:00
|
|
|
return -EINVAL;
|
|
|
|
}
|
|
|
|
length = be32_to_cpu(length);
|
2018-01-11 00:08:24 +01:00
|
|
|
assert(!client->optlen);
|
2018-01-11 00:08:21 +01:00
|
|
|
client->optlen = length;
|
2014-06-07 02:32:31 +02:00
|
|
|
|
nbd/server: CVE-2017-15119 Reject options larger than 32M
The NBD spec gives us permission to abruptly disconnect on clients
that send outrageously large option requests, rather than having
to spend the time reading to the end of the option. No real
option request requires that much data anyways; and meanwhile, we
already have the practice of abruptly dropping the connection on
any client that sends NBD_CMD_WRITE with a payload larger than 32M.
For comparison, nbdkit drops the connection on any request with
more than 4096 bytes; however, that limit is probably too low
(as the NBD spec states an export name can theoretically be up
to 4096 bytes, which means a valid NBD_OPT_INFO could be even
longer) - even if qemu doesn't permit exports longer than 256
bytes.
It could be argued that a malicious client trying to get us to
read nearly 4G of data on a bad request is a form of denial of
service. In particular, if the server requires TLS, but a client
that does not know the TLS credentials sends any option (other
than NBD_OPT_STARTTLS or NBD_OPT_EXPORT_NAME) with a stated
payload of nearly 4G, then the server was keeping the connection
alive trying to read all the payload, tying up resources that it
would rather be spending on a client that can get past the TLS
handshake. Hence, this warranted a CVE.
Present since at least 2.5 when handling known options, and made
worse in 2.6 when fixing support for NBD_FLAG_C_FIXED_NEWSTYLE
to handle unknown options.
CC: qemu-stable@nongnu.org
Signed-off-by: Eric Blake <eblake@redhat.com>
2017-11-22 23:25:16 +01:00
|
|
|
if (length > NBD_MAX_BUFFER_SIZE) {
|
|
|
|
error_setg(errp, "len (%" PRIu32" ) is larger than max len (%u)",
|
|
|
|
length, NBD_MAX_BUFFER_SIZE);
|
|
|
|
return -EINVAL;
|
|
|
|
}
|
|
|
|
|
2017-07-07 22:30:43 +02:00
|
|
|
trace_nbd_negotiate_options_check_option(option,
|
|
|
|
nbd_opt_lookup(option));
|
2016-02-10 19:41:11 +01:00
|
|
|
if (client->tlscreds &&
|
|
|
|
client->ioc == (QIOChannel *)client->sioc) {
|
|
|
|
QIOChannel *tioc;
|
|
|
|
if (!fixedNewstyle) {
|
2017-07-07 17:29:16 +02:00
|
|
|
error_setg(errp, "Unsupported option 0x%" PRIx32, option);
|
2016-02-10 19:41:11 +01:00
|
|
|
return -EINVAL;
|
|
|
|
}
|
2017-07-07 17:29:16 +02:00
|
|
|
switch (option) {
|
2016-02-10 19:41:11 +01:00
|
|
|
case NBD_OPT_STARTTLS:
|
2017-10-27 12:40:31 +02:00
|
|
|
if (length) {
|
|
|
|
/* Unconditionally drop the connection if the client
|
|
|
|
* can't start a TLS negotiation correctly */
|
2018-01-11 00:08:21 +01:00
|
|
|
return nbd_reject_length(client, true, errp);
|
2017-10-27 12:40:31 +02:00
|
|
|
}
|
|
|
|
tioc = nbd_negotiate_handle_starttls(client, errp);
|
2016-02-10 19:41:11 +01:00
|
|
|
if (!tioc) {
|
|
|
|
return -EIO;
|
|
|
|
}
|
2017-10-27 12:40:30 +02:00
|
|
|
ret = 0;
|
2016-02-10 19:41:11 +01:00
|
|
|
object_unref(OBJECT(client->ioc));
|
|
|
|
client->ioc = QIO_CHANNEL(tioc);
|
|
|
|
break;
|
|
|
|
|
nbd: Don't kill server on client that doesn't request TLS
Upstream NBD documents (as of commit 4feebc95) that servers MAY
choose to operate in a conditional mode, where it is up to the
client whether to use TLS. For qemu's case, we want to always be
in FORCEDTLS mode, because of the risk of man-in-the-middle
attacks, and since we never export more than one device; likewise,
the qemu client will ALWAYS send NBD_OPT_STARTTLS as its first
option. But now that SELECTIVETLS servers exist, it is feasible
to encounter a (non-qemu) client that is programmed to talk to
such a server, and does not do NBD_OPT_STARTTLS first, but rather
wants to probe if it can use a non-encrypted export.
The NBD protocol documents that we should let such a client
continue trying, on the grounds that maybe the client will get the
hint to send NBD_OPT_STARTTLS, rather than immediately dropping
the connection.
Note that NBD_OPT_EXPORT_NAME is a special case: since it is the
only option request that can't have an error return, we have to
(continue to) drop the connection on that one; rather, what we are
fixing here is that all other replies prior to TLS initiation tell
the client NBD_REP_ERR_TLS_REQD, but keep the connection alive.
Signed-off-by: Eric Blake <eblake@redhat.com>
Message-id: 1460671343-18485-1-git-send-email-eblake@redhat.com
Signed-off-by: Max Reitz <mreitz@redhat.com>
2016-04-15 00:02:23 +02:00
|
|
|
case NBD_OPT_EXPORT_NAME:
|
|
|
|
/* No way to return an error to client, so drop connection */
|
2017-07-07 17:29:11 +02:00
|
|
|
error_setg(errp, "Option 0x%x not permitted before TLS",
|
2017-07-07 17:29:16 +02:00
|
|
|
option);
|
nbd: Don't kill server on client that doesn't request TLS
Upstream NBD documents (as of commit 4feebc95) that servers MAY
choose to operate in a conditional mode, where it is up to the
client whether to use TLS. For qemu's case, we want to always be
in FORCEDTLS mode, because of the risk of man-in-the-middle
attacks, and since we never export more than one device; likewise,
the qemu client will ALWAYS send NBD_OPT_STARTTLS as its first
option. But now that SELECTIVETLS servers exist, it is feasible
to encounter a (non-qemu) client that is programmed to talk to
such a server, and does not do NBD_OPT_STARTTLS first, but rather
wants to probe if it can use a non-encrypted export.
The NBD protocol documents that we should let such a client
continue trying, on the grounds that maybe the client will get the
hint to send NBD_OPT_STARTTLS, rather than immediately dropping
the connection.
Note that NBD_OPT_EXPORT_NAME is a special case: since it is the
only option request that can't have an error return, we have to
(continue to) drop the connection on that one; rather, what we are
fixing here is that all other replies prior to TLS initiation tell
the client NBD_REP_ERR_TLS_REQD, but keep the connection alive.
Signed-off-by: Eric Blake <eblake@redhat.com>
Message-id: 1460671343-18485-1-git-send-email-eblake@redhat.com
Signed-off-by: Max Reitz <mreitz@redhat.com>
2016-04-15 00:02:23 +02:00
|
|
|
return -EINVAL;
|
|
|
|
|
2016-02-10 19:41:11 +01:00
|
|
|
default:
|
2018-01-11 00:08:24 +01:00
|
|
|
ret = nbd_opt_drop(client, NBD_REP_ERR_TLS_REQD, errp,
|
|
|
|
"Option 0x%" PRIx32
|
|
|
|
"not permitted before TLS", option);
|
2017-07-07 22:30:42 +02:00
|
|
|
/* Let the client keep trying, unless they asked to
|
|
|
|
* quit. In this mode, we've already sent an error, so
|
|
|
|
* we can't ack the abort. */
|
2017-07-07 17:29:16 +02:00
|
|
|
if (option == NBD_OPT_ABORT) {
|
2017-07-07 17:29:09 +02:00
|
|
|
return 1;
|
2016-10-14 20:33:16 +02:00
|
|
|
}
|
nbd: Don't kill server on client that doesn't request TLS
Upstream NBD documents (as of commit 4feebc95) that servers MAY
choose to operate in a conditional mode, where it is up to the
client whether to use TLS. For qemu's case, we want to always be
in FORCEDTLS mode, because of the risk of man-in-the-middle
attacks, and since we never export more than one device; likewise,
the qemu client will ALWAYS send NBD_OPT_STARTTLS as its first
option. But now that SELECTIVETLS servers exist, it is feasible
to encounter a (non-qemu) client that is programmed to talk to
such a server, and does not do NBD_OPT_STARTTLS first, but rather
wants to probe if it can use a non-encrypted export.
The NBD protocol documents that we should let such a client
continue trying, on the grounds that maybe the client will get the
hint to send NBD_OPT_STARTTLS, rather than immediately dropping
the connection.
Note that NBD_OPT_EXPORT_NAME is a special case: since it is the
only option request that can't have an error return, we have to
(continue to) drop the connection on that one; rather, what we are
fixing here is that all other replies prior to TLS initiation tell
the client NBD_REP_ERR_TLS_REQD, but keep the connection alive.
Signed-off-by: Eric Blake <eblake@redhat.com>
Message-id: 1460671343-18485-1-git-send-email-eblake@redhat.com
Signed-off-by: Max Reitz <mreitz@redhat.com>
2016-04-15 00:02:23 +02:00
|
|
|
break;
|
2016-02-10 19:41:11 +01:00
|
|
|
}
|
|
|
|
} else if (fixedNewstyle) {
|
2017-07-07 17:29:16 +02:00
|
|
|
switch (option) {
|
2016-02-10 19:41:06 +01:00
|
|
|
case NBD_OPT_LIST:
|
2017-10-27 12:40:31 +02:00
|
|
|
if (length) {
|
2018-01-11 00:08:21 +01:00
|
|
|
ret = nbd_reject_length(client, false, errp);
|
2017-10-27 12:40:31 +02:00
|
|
|
} else {
|
|
|
|
ret = nbd_negotiate_handle_list(client, errp);
|
|
|
|
}
|
2016-02-10 19:41:06 +01:00
|
|
|
break;
|
|
|
|
|
|
|
|
case NBD_OPT_ABORT:
|
2016-10-14 20:33:16 +02:00
|
|
|
/* NBD spec says we must try to reply before
|
|
|
|
* disconnecting, but that we must also tolerate
|
|
|
|
* guests that don't wait for our reply. */
|
2018-01-11 00:08:21 +01:00
|
|
|
nbd_negotiate_send_rep(client, NBD_REP_ACK, NULL);
|
2017-07-07 17:29:09 +02:00
|
|
|
return 1;
|
2016-02-10 19:41:06 +01:00
|
|
|
|
|
|
|
case NBD_OPT_EXPORT_NAME:
|
2018-01-11 00:08:21 +01:00
|
|
|
return nbd_negotiate_handle_export_name(client,
|
2017-07-07 22:30:45 +02:00
|
|
|
myflags, no_zeroes,
|
|
|
|
errp);
|
2016-02-10 19:41:06 +01:00
|
|
|
|
2017-07-07 22:30:46 +02:00
|
|
|
case NBD_OPT_INFO:
|
|
|
|
case NBD_OPT_GO:
|
2018-01-11 00:08:21 +01:00
|
|
|
ret = nbd_negotiate_handle_info(client, myflags, errp);
|
2017-07-07 22:30:46 +02:00
|
|
|
if (ret == 1) {
|
|
|
|
assert(option == NBD_OPT_GO);
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
break;
|
|
|
|
|
2016-02-10 19:41:11 +01:00
|
|
|
case NBD_OPT_STARTTLS:
|
2017-10-27 12:40:31 +02:00
|
|
|
if (length) {
|
2018-01-11 00:08:21 +01:00
|
|
|
ret = nbd_reject_length(client, false, errp);
|
2017-10-27 12:40:31 +02:00
|
|
|
} else if (client->tlscreds) {
|
2018-01-11 00:08:21 +01:00
|
|
|
ret = nbd_negotiate_send_rep_err(client,
|
|
|
|
NBD_REP_ERR_INVALID, errp,
|
2016-10-14 20:33:09 +02:00
|
|
|
"TLS already enabled");
|
2016-02-10 19:41:11 +01:00
|
|
|
} else {
|
2018-01-11 00:08:21 +01:00
|
|
|
ret = nbd_negotiate_send_rep_err(client,
|
|
|
|
NBD_REP_ERR_POLICY, errp,
|
2016-10-14 20:33:09 +02:00
|
|
|
"TLS not configured");
|
2016-05-12 00:39:36 +02:00
|
|
|
}
|
nbd: Don't kill server on client that doesn't request TLS
Upstream NBD documents (as of commit 4feebc95) that servers MAY
choose to operate in a conditional mode, where it is up to the
client whether to use TLS. For qemu's case, we want to always be
in FORCEDTLS mode, because of the risk of man-in-the-middle
attacks, and since we never export more than one device; likewise,
the qemu client will ALWAYS send NBD_OPT_STARTTLS as its first
option. But now that SELECTIVETLS servers exist, it is feasible
to encounter a (non-qemu) client that is programmed to talk to
such a server, and does not do NBD_OPT_STARTTLS first, but rather
wants to probe if it can use a non-encrypted export.
The NBD protocol documents that we should let such a client
continue trying, on the grounds that maybe the client will get the
hint to send NBD_OPT_STARTTLS, rather than immediately dropping
the connection.
Note that NBD_OPT_EXPORT_NAME is a special case: since it is the
only option request that can't have an error return, we have to
(continue to) drop the connection on that one; rather, what we are
fixing here is that all other replies prior to TLS initiation tell
the client NBD_REP_ERR_TLS_REQD, but keep the connection alive.
Signed-off-by: Eric Blake <eblake@redhat.com>
Message-id: 1460671343-18485-1-git-send-email-eblake@redhat.com
Signed-off-by: Max Reitz <mreitz@redhat.com>
2016-04-15 00:02:23 +02:00
|
|
|
break;
|
2017-10-27 12:40:32 +02:00
|
|
|
|
|
|
|
case NBD_OPT_STRUCTURED_REPLY:
|
|
|
|
if (length) {
|
2018-01-11 00:08:21 +01:00
|
|
|
ret = nbd_reject_length(client, false, errp);
|
2017-10-27 12:40:32 +02:00
|
|
|
} else if (client->structured_reply) {
|
|
|
|
ret = nbd_negotiate_send_rep_err(
|
2018-01-11 00:08:21 +01:00
|
|
|
client, NBD_REP_ERR_INVALID, errp,
|
2017-10-27 12:40:32 +02:00
|
|
|
"structured reply already negotiated");
|
|
|
|
} else {
|
2018-01-11 00:08:21 +01:00
|
|
|
ret = nbd_negotiate_send_rep(client, NBD_REP_ACK, errp);
|
2017-10-27 12:40:32 +02:00
|
|
|
client->structured_reply = true;
|
|
|
|
myflags |= NBD_FLAG_SEND_DF;
|
|
|
|
}
|
|
|
|
break;
|
|
|
|
|
2018-03-12 16:21:21 +01:00
|
|
|
case NBD_OPT_LIST_META_CONTEXT:
|
|
|
|
case NBD_OPT_SET_META_CONTEXT:
|
|
|
|
ret = nbd_negotiate_meta_queries(client, &client->export_meta,
|
|
|
|
errp);
|
|
|
|
break;
|
|
|
|
|
2016-02-10 19:41:06 +01:00
|
|
|
default:
|
2018-01-11 00:08:24 +01:00
|
|
|
ret = nbd_opt_drop(client, NBD_REP_ERR_UNSUP, errp,
|
2018-02-15 14:51:43 +01:00
|
|
|
"Unsupported option %" PRIu32 " (%s)",
|
2018-01-11 00:08:24 +01:00
|
|
|
option, nbd_opt_lookup(option));
|
2016-04-07 00:48:38 +02:00
|
|
|
break;
|
2016-02-10 19:41:06 +01:00
|
|
|
}
|
|
|
|
} else {
|
|
|
|
/*
|
|
|
|
* If broken new-style we should drop the connection
|
|
|
|
* for anything except NBD_OPT_EXPORT_NAME
|
|
|
|
*/
|
2017-07-07 17:29:16 +02:00
|
|
|
switch (option) {
|
2016-02-10 19:41:06 +01:00
|
|
|
case NBD_OPT_EXPORT_NAME:
|
2018-01-11 00:08:21 +01:00
|
|
|
return nbd_negotiate_handle_export_name(client,
|
2017-07-07 22:30:45 +02:00
|
|
|
myflags, no_zeroes,
|
|
|
|
errp);
|
2016-02-10 19:41:06 +01:00
|
|
|
|
|
|
|
default:
|
2018-02-15 14:51:43 +01:00
|
|
|
error_setg(errp, "Unsupported option %" PRIu32 " (%s)",
|
2017-07-07 22:30:43 +02:00
|
|
|
option, nbd_opt_lookup(option));
|
2016-02-10 19:41:06 +01:00
|
|
|
return -EINVAL;
|
2014-06-07 02:32:32 +02:00
|
|
|
}
|
2014-06-07 02:32:31 +02:00
|
|
|
}
|
2017-10-27 12:40:30 +02:00
|
|
|
if (ret < 0) {
|
|
|
|
return ret;
|
|
|
|
}
|
2014-06-07 02:32:31 +02:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2017-07-07 17:29:09 +02:00
|
|
|
/* nbd_negotiate
|
|
|
|
* Return:
|
2017-07-07 17:29:11 +02:00
|
|
|
* -errno on error, errp is set
|
|
|
|
* 0 on successful negotiation, errp is not set
|
|
|
|
* 1 if client sent NBD_OPT_ABORT, i.e. on valid disconnect,
|
|
|
|
* errp is not set
|
2017-07-07 17:29:09 +02:00
|
|
|
*/
|
2017-07-07 17:29:11 +02:00
|
|
|
static coroutine_fn int nbd_negotiate(NBDClient *client, Error **errp)
|
2008-05-27 23:13:40 +02:00
|
|
|
{
|
2017-07-17 21:26:35 +02:00
|
|
|
char buf[NBD_OLDSTYLE_NEGOTIATE_SIZE] = "";
|
2017-06-02 17:01:49 +02:00
|
|
|
int ret;
|
2016-07-21 21:34:46 +02:00
|
|
|
const uint16_t myflags = (NBD_FLAG_HAS_FLAGS | NBD_FLAG_SEND_TRIM |
|
2016-10-14 20:33:17 +02:00
|
|
|
NBD_FLAG_SEND_FLUSH | NBD_FLAG_SEND_FUA |
|
|
|
|
NBD_FLAG_SEND_WRITE_ZEROES);
|
2016-02-10 19:41:11 +01:00
|
|
|
bool oldStyle;
|
2011-02-22 16:44:51 +01:00
|
|
|
|
2017-07-17 21:26:35 +02:00
|
|
|
/* Old style negotiation header, no room for options
|
2012-08-23 14:57:11 +02:00
|
|
|
[ 0 .. 7] passwd ("NBDMAGIC")
|
|
|
|
[ 8 .. 15] magic (NBD_CLIENT_MAGIC)
|
2011-02-22 16:44:51 +01:00
|
|
|
[16 .. 23] size
|
2017-07-17 21:26:35 +02:00
|
|
|
[24 .. 27] export flags (zero-extended)
|
2012-08-23 14:57:11 +02:00
|
|
|
[28 .. 151] reserved (0)
|
|
|
|
|
2017-07-17 21:26:35 +02:00
|
|
|
New style negotiation header, client can send options
|
2012-08-23 14:57:11 +02:00
|
|
|
[ 0 .. 7] passwd ("NBDMAGIC")
|
|
|
|
[ 8 .. 15] magic (NBD_OPTS_MAGIC)
|
|
|
|
[16 .. 17] server flags (0)
|
2017-07-07 22:30:46 +02:00
|
|
|
....options sent, ending in NBD_OPT_EXPORT_NAME or NBD_OPT_GO....
|
2011-02-22 16:44:51 +01:00
|
|
|
*/
|
|
|
|
|
2016-02-10 19:41:04 +01:00
|
|
|
qio_channel_set_blocking(client->ioc, false, NULL);
|
2012-03-05 08:56:10 +01:00
|
|
|
|
2017-07-07 17:29:18 +02:00
|
|
|
trace_nbd_negotiate_begin();
|
2011-02-22 16:44:51 +01:00
|
|
|
memcpy(buf, "NBDMAGIC", 8);
|
2016-02-10 19:41:11 +01:00
|
|
|
|
|
|
|
oldStyle = client->exp != NULL && !client->tlscreds;
|
|
|
|
if (oldStyle) {
|
2017-07-07 17:29:18 +02:00
|
|
|
trace_nbd_negotiate_old_style(client->exp->size,
|
|
|
|
client->exp->nbdflags | myflags);
|
2016-02-04 11:27:55 +01:00
|
|
|
stq_be_p(buf + 8, NBD_CLIENT_MAGIC);
|
|
|
|
stq_be_p(buf + 16, client->exp->size);
|
2017-07-17 21:26:35 +02:00
|
|
|
stl_be_p(buf + 24, client->exp->nbdflags | myflags);
|
2011-02-22 16:44:51 +01:00
|
|
|
|
2017-07-07 17:29:11 +02:00
|
|
|
if (nbd_write(client->ioc, buf, sizeof(buf), errp) < 0) {
|
|
|
|
error_prepend(errp, "write failed: ");
|
2017-06-02 17:01:48 +02:00
|
|
|
return -EINVAL;
|
2012-08-23 14:57:11 +02:00
|
|
|
}
|
|
|
|
} else {
|
2017-07-07 17:29:10 +02:00
|
|
|
stq_be_p(buf + 8, NBD_OPTS_MAGIC);
|
|
|
|
stw_be_p(buf + 16, NBD_FLAG_FIXED_NEWSTYLE | NBD_FLAG_NO_ZEROES);
|
|
|
|
|
2017-07-07 17:29:11 +02:00
|
|
|
if (nbd_write(client->ioc, buf, 18, errp) < 0) {
|
|
|
|
error_prepend(errp, "write failed: ");
|
2017-06-02 17:01:48 +02:00
|
|
|
return -EINVAL;
|
2012-08-23 14:57:11 +02:00
|
|
|
}
|
2017-07-07 22:30:45 +02:00
|
|
|
ret = nbd_negotiate_options(client, myflags, errp);
|
2017-06-02 17:01:49 +02:00
|
|
|
if (ret != 0) {
|
2017-07-07 17:29:11 +02:00
|
|
|
if (ret < 0) {
|
|
|
|
error_prepend(errp, "option negotiation failed: ");
|
|
|
|
}
|
2017-06-02 17:01:49 +02:00
|
|
|
return ret;
|
2012-08-23 14:57:11 +02:00
|
|
|
}
|
2011-02-22 16:44:51 +01:00
|
|
|
}
|
|
|
|
|
2018-01-11 00:08:21 +01:00
|
|
|
assert(!client->optlen);
|
2017-07-07 17:29:18 +02:00
|
|
|
trace_nbd_negotiate_success();
|
2017-06-02 17:01:48 +02:00
|
|
|
|
|
|
|
return 0;
|
2008-05-27 23:13:40 +02:00
|
|
|
}
|
|
|
|
|
2017-07-07 17:29:11 +02:00
|
|
|
static int nbd_receive_request(QIOChannel *ioc, NBDRequest *request,
|
|
|
|
Error **errp)
|
2008-07-03 15:41:03 +02:00
|
|
|
{
|
2012-08-22 15:13:30 +02:00
|
|
|
uint8_t buf[NBD_REQUEST_SIZE];
|
2011-02-22 16:44:51 +01:00
|
|
|
uint32_t magic;
|
2017-06-02 17:01:42 +02:00
|
|
|
int ret;
|
2011-02-22 16:44:51 +01:00
|
|
|
|
2017-07-07 17:29:11 +02:00
|
|
|
ret = nbd_read(ioc, buf, sizeof(buf), errp);
|
2012-03-05 08:56:10 +01:00
|
|
|
if (ret < 0) {
|
|
|
|
return ret;
|
|
|
|
}
|
|
|
|
|
2011-02-22 16:44:51 +01:00
|
|
|
/* Request
|
|
|
|
[ 0 .. 3] magic (NBD_REQUEST_MAGIC)
|
2016-10-14 20:33:04 +02:00
|
|
|
[ 4 .. 5] flags (NBD_CMD_FLAG_FUA, ...)
|
|
|
|
[ 6 .. 7] type (NBD_CMD_READ, ...)
|
2011-02-22 16:44:51 +01:00
|
|
|
[ 8 .. 15] handle
|
|
|
|
[16 .. 23] from
|
|
|
|
[24 .. 27] len
|
|
|
|
*/
|
|
|
|
|
2016-06-10 17:00:36 +02:00
|
|
|
magic = ldl_be_p(buf);
|
2016-10-14 20:33:04 +02:00
|
|
|
request->flags = lduw_be_p(buf + 4);
|
|
|
|
request->type = lduw_be_p(buf + 6);
|
2016-06-10 17:00:36 +02:00
|
|
|
request->handle = ldq_be_p(buf + 8);
|
|
|
|
request->from = ldq_be_p(buf + 16);
|
|
|
|
request->len = ldl_be_p(buf + 24);
|
2011-02-22 16:44:51 +01:00
|
|
|
|
2017-07-07 17:29:18 +02:00
|
|
|
trace_nbd_receive_request(magic, request->flags, request->type,
|
|
|
|
request->from, request->len);
|
2011-02-22 16:44:51 +01:00
|
|
|
|
|
|
|
if (magic != NBD_REQUEST_MAGIC) {
|
2017-07-07 17:29:11 +02:00
|
|
|
error_setg(errp, "invalid magic (got 0x%" PRIx32 ")", magic);
|
2012-03-05 08:56:10 +01:00
|
|
|
return -EINVAL;
|
2011-02-22 16:44:51 +01:00
|
|
|
}
|
|
|
|
return 0;
|
2008-07-03 15:41:03 +02:00
|
|
|
}
|
|
|
|
|
2011-09-19 15:25:40 +02:00
|
|
|
#define MAX_NBD_REQUESTS 16
|
|
|
|
|
2012-09-18 13:17:52 +02:00
|
|
|
void nbd_client_get(NBDClient *client)
|
2011-09-19 14:33:23 +02:00
|
|
|
{
|
|
|
|
client->refcount++;
|
|
|
|
}
|
|
|
|
|
2012-09-18 13:17:52 +02:00
|
|
|
void nbd_client_put(NBDClient *client)
|
2011-09-19 14:33:23 +02:00
|
|
|
{
|
|
|
|
if (--client->refcount == 0) {
|
2012-08-22 18:45:12 +02:00
|
|
|
/* The last reference should be dropped by client->close,
|
2015-02-06 22:06:16 +01:00
|
|
|
* which is called by client_close.
|
2012-08-22 18:45:12 +02:00
|
|
|
*/
|
|
|
|
assert(client->closing);
|
|
|
|
|
2017-02-13 14:52:24 +01:00
|
|
|
qio_channel_detach_aio_context(client->ioc);
|
2016-02-10 19:41:04 +01:00
|
|
|
object_unref(OBJECT(client->sioc));
|
|
|
|
object_unref(OBJECT(client->ioc));
|
2016-02-10 19:41:11 +01:00
|
|
|
if (client->tlscreds) {
|
|
|
|
object_unref(OBJECT(client->tlscreds));
|
|
|
|
}
|
|
|
|
g_free(client->tlsaclname);
|
2012-08-23 14:57:11 +02:00
|
|
|
if (client->exp) {
|
|
|
|
QTAILQ_REMOVE(&client->exp->clients, client, next);
|
|
|
|
nbd_export_put(client->exp);
|
|
|
|
}
|
2011-09-19 14:33:23 +02:00
|
|
|
g_free(client);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
nbd: Fix regression on resiliency to port scan
Back in qemu 2.5, qemu-nbd was immune to port probes (a transient
server would not quit, regardless of how many probe connections
came and went, until a connection actually negotiated). But we
broke that in commit ee7d7aa when removing the return value to
nbd_client_new(), although that patch also introduced a bug causing
an assertion failure on a client that fails negotiation. We then
made it worse during refactoring in commit 1a6245a (a segfault
before we could even assert); the (masked) assertion was cleaned
up in d3780c2 (still in 2.6), and just recently we finally fixed
the segfault ("nbd: Fully intialize client in case of failed
negotiation"). But that still means that ever since we added
TLS support to qemu-nbd, we have been vulnerable to an ill-timed
port-scan being able to cause a denial of service by taking down
qemu-nbd before a real client has a chance to connect.
Since negotiation is now handled asynchronously via coroutines,
we no longer have a synchronous point of return by re-adding a
return value to nbd_client_new(). So this patch instead wires
things up to pass the negotiation status through the close_fn
callback function.
Simple test across two terminals:
$ qemu-nbd -f raw -p 30001 file
$ nmap 127.0.0.1 -p 30001 && \
qemu-io -c 'r 0 512' -f raw nbd://localhost:30001
Note that this patch does not change what constitutes successful
negotiation (thus, a client must enter transmission phase before
that client can be considered as a reason to terminate the server
when the connection ends). Perhaps we may want to tweak things
in a later patch to also treat a client that uses NBD_OPT_ABORT
as being a 'successful' negotiation (the client correctly talked
the NBD protocol, and informed us it was not going to use our
export after all), but that's a discussion for another day.
Fixes: https://bugzilla.redhat.com/show_bug.cgi?id=1451614
Signed-off-by: Eric Blake <eblake@redhat.com>
Message-Id: <20170608222617.20376-1-eblake@redhat.com>
Signed-off-by: Paolo Bonzini <pbonzini@redhat.com>
2017-06-09 00:26:17 +02:00
|
|
|
static void client_close(NBDClient *client, bool negotiated)
|
2011-09-19 14:33:23 +02:00
|
|
|
{
|
2012-08-22 18:45:12 +02:00
|
|
|
if (client->closing) {
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
client->closing = true;
|
|
|
|
|
|
|
|
/* Force requests to finish. They will drop their own references,
|
|
|
|
* then we'll close the socket and free the NBDClient.
|
|
|
|
*/
|
2016-02-10 19:41:04 +01:00
|
|
|
qio_channel_shutdown(client->ioc, QIO_CHANNEL_SHUTDOWN_BOTH,
|
|
|
|
NULL);
|
2012-08-22 18:45:12 +02:00
|
|
|
|
|
|
|
/* Also tell the client, so that they release their reference. */
|
nbd: Fix regression on resiliency to port scan
Back in qemu 2.5, qemu-nbd was immune to port probes (a transient
server would not quit, regardless of how many probe connections
came and went, until a connection actually negotiated). But we
broke that in commit ee7d7aa when removing the return value to
nbd_client_new(), although that patch also introduced a bug causing
an assertion failure on a client that fails negotiation. We then
made it worse during refactoring in commit 1a6245a (a segfault
before we could even assert); the (masked) assertion was cleaned
up in d3780c2 (still in 2.6), and just recently we finally fixed
the segfault ("nbd: Fully intialize client in case of failed
negotiation"). But that still means that ever since we added
TLS support to qemu-nbd, we have been vulnerable to an ill-timed
port-scan being able to cause a denial of service by taking down
qemu-nbd before a real client has a chance to connect.
Since negotiation is now handled asynchronously via coroutines,
we no longer have a synchronous point of return by re-adding a
return value to nbd_client_new(). So this patch instead wires
things up to pass the negotiation status through the close_fn
callback function.
Simple test across two terminals:
$ qemu-nbd -f raw -p 30001 file
$ nmap 127.0.0.1 -p 30001 && \
qemu-io -c 'r 0 512' -f raw nbd://localhost:30001
Note that this patch does not change what constitutes successful
negotiation (thus, a client must enter transmission phase before
that client can be considered as a reason to terminate the server
when the connection ends). Perhaps we may want to tweak things
in a later patch to also treat a client that uses NBD_OPT_ABORT
as being a 'successful' negotiation (the client correctly talked
the NBD protocol, and informed us it was not going to use our
export after all), but that's a discussion for another day.
Fixes: https://bugzilla.redhat.com/show_bug.cgi?id=1451614
Signed-off-by: Eric Blake <eblake@redhat.com>
Message-Id: <20170608222617.20376-1-eblake@redhat.com>
Signed-off-by: Paolo Bonzini <pbonzini@redhat.com>
2017-06-09 00:26:17 +02:00
|
|
|
if (client->close_fn) {
|
|
|
|
client->close_fn(client, negotiated);
|
2011-09-19 14:33:23 +02:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2016-10-14 20:33:05 +02:00
|
|
|
static NBDRequestData *nbd_request_get(NBDClient *client)
|
2011-09-19 14:18:33 +02:00
|
|
|
{
|
2016-10-14 20:33:05 +02:00
|
|
|
NBDRequestData *req;
|
2011-10-07 16:47:56 +02:00
|
|
|
|
2011-09-19 15:25:40 +02:00
|
|
|
assert(client->nb_requests <= MAX_NBD_REQUESTS - 1);
|
|
|
|
client->nb_requests++;
|
|
|
|
|
2016-10-14 20:33:05 +02:00
|
|
|
req = g_new0(NBDRequestData, 1);
|
2011-10-07 16:47:56 +02:00
|
|
|
nbd_client_get(client);
|
|
|
|
req->client = client;
|
2011-09-19 14:18:33 +02:00
|
|
|
return req;
|
|
|
|
}
|
|
|
|
|
2016-10-14 20:33:05 +02:00
|
|
|
static void nbd_request_put(NBDRequestData *req)
|
2011-09-19 14:18:33 +02:00
|
|
|
{
|
2011-10-07 16:47:56 +02:00
|
|
|
NBDClient *client = req->client;
|
2013-05-02 14:23:07 +02:00
|
|
|
|
2013-05-02 14:23:08 +02:00
|
|
|
if (req->data) {
|
|
|
|
qemu_vfree(req->data);
|
|
|
|
}
|
2015-10-01 12:59:08 +02:00
|
|
|
g_free(req);
|
2013-05-02 14:23:07 +02:00
|
|
|
|
2014-06-20 21:57:32 +02:00
|
|
|
client->nb_requests--;
|
2017-02-13 14:52:24 +01:00
|
|
|
nbd_client_receive_next_request(client);
|
|
|
|
|
2011-10-07 16:47:56 +02:00
|
|
|
nbd_client_put(client);
|
2011-09-19 14:18:33 +02:00
|
|
|
}
|
|
|
|
|
2014-11-18 12:21:18 +01:00
|
|
|
static void blk_aio_attached(AioContext *ctx, void *opaque)
|
2014-06-20 21:57:34 +02:00
|
|
|
{
|
|
|
|
NBDExport *exp = opaque;
|
|
|
|
NBDClient *client;
|
|
|
|
|
2017-07-07 17:29:18 +02:00
|
|
|
trace_nbd_blk_aio_attached(exp->name, ctx);
|
2014-06-20 21:57:34 +02:00
|
|
|
|
|
|
|
exp->ctx = ctx;
|
|
|
|
|
|
|
|
QTAILQ_FOREACH(client, &exp->clients, next) {
|
2017-02-13 14:52:24 +01:00
|
|
|
qio_channel_attach_aio_context(client->ioc, ctx);
|
|
|
|
if (client->recv_coroutine) {
|
|
|
|
aio_co_schedule(ctx, client->recv_coroutine);
|
|
|
|
}
|
|
|
|
if (client->send_coroutine) {
|
|
|
|
aio_co_schedule(ctx, client->send_coroutine);
|
|
|
|
}
|
2014-06-20 21:57:34 +02:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2014-11-18 12:21:18 +01:00
|
|
|
static void blk_aio_detach(void *opaque)
|
2014-06-20 21:57:34 +02:00
|
|
|
{
|
|
|
|
NBDExport *exp = opaque;
|
|
|
|
NBDClient *client;
|
|
|
|
|
2017-07-07 17:29:18 +02:00
|
|
|
trace_nbd_blk_aio_detach(exp->name, exp->ctx);
|
2014-06-20 21:57:34 +02:00
|
|
|
|
|
|
|
QTAILQ_FOREACH(client, &exp->clients, next) {
|
2017-02-13 14:52:24 +01:00
|
|
|
qio_channel_detach_aio_context(client->ioc);
|
2014-06-20 21:57:34 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
exp->ctx = NULL;
|
|
|
|
}
|
|
|
|
|
2016-01-29 16:36:06 +01:00
|
|
|
static void nbd_eject_notifier(Notifier *n, void *data)
|
|
|
|
{
|
|
|
|
NBDExport *exp = container_of(n, NBDExport, eject_notifier);
|
|
|
|
nbd_export_close(exp);
|
|
|
|
}
|
|
|
|
|
2016-07-06 11:22:39 +02:00
|
|
|
NBDExport *nbd_export_new(BlockDriverState *bs, off_t dev_offset, off_t size,
|
2016-07-21 21:34:46 +02:00
|
|
|
uint16_t nbdflags, void (*close)(NBDExport *),
|
2016-07-06 11:22:39 +02:00
|
|
|
bool writethrough, BlockBackend *on_eject_blk,
|
2015-02-25 19:08:21 +01:00
|
|
|
Error **errp)
|
2011-09-19 14:03:37 +02:00
|
|
|
{
|
2017-08-15 15:07:38 +02:00
|
|
|
AioContext *ctx;
|
2016-07-06 11:22:39 +02:00
|
|
|
BlockBackend *blk;
|
2017-10-07 01:49:16 +02:00
|
|
|
NBDExport *exp = g_new0(NBDExport, 1);
|
2017-02-09 15:43:38 +01:00
|
|
|
uint64_t perm;
|
2017-01-13 19:02:32 +01:00
|
|
|
int ret;
|
2016-07-06 11:22:39 +02:00
|
|
|
|
2017-08-15 15:07:38 +02:00
|
|
|
/*
|
|
|
|
* NBD exports are used for non-shared storage migration. Make sure
|
|
|
|
* that BDRV_O_INACTIVE is cleared and the image is ready for write
|
|
|
|
* access since the export could be available before migration handover.
|
|
|
|
*/
|
|
|
|
ctx = bdrv_get_aio_context(bs);
|
|
|
|
aio_context_acquire(ctx);
|
|
|
|
bdrv_invalidate_cache(bs, NULL);
|
|
|
|
aio_context_release(ctx);
|
|
|
|
|
2017-02-09 15:43:38 +01:00
|
|
|
/* Don't allow resize while the NBD server is running, otherwise we don't
|
|
|
|
* care what happens with the node. */
|
|
|
|
perm = BLK_PERM_CONSISTENT_READ;
|
|
|
|
if ((nbdflags & NBD_FLAG_READ_ONLY) == 0) {
|
|
|
|
perm |= BLK_PERM_WRITE;
|
|
|
|
}
|
|
|
|
blk = blk_new(perm, BLK_PERM_CONSISTENT_READ | BLK_PERM_WRITE_UNCHANGED |
|
|
|
|
BLK_PERM_WRITE | BLK_PERM_GRAPH_MOD);
|
2017-01-13 19:02:32 +01:00
|
|
|
ret = blk_insert_bs(blk, bs, errp);
|
|
|
|
if (ret < 0) {
|
|
|
|
goto fail;
|
|
|
|
}
|
2016-07-06 11:22:39 +02:00
|
|
|
blk_set_enable_write_cache(blk, !writethrough);
|
|
|
|
|
2012-09-18 13:26:25 +02:00
|
|
|
exp->refcount = 1;
|
2012-09-18 13:58:25 +02:00
|
|
|
QTAILQ_INIT(&exp->clients);
|
2014-11-18 12:21:18 +01:00
|
|
|
exp->blk = blk;
|
2011-09-19 14:03:37 +02:00
|
|
|
exp->dev_offset = dev_offset;
|
|
|
|
exp->nbdflags = nbdflags;
|
2015-02-25 19:08:21 +01:00
|
|
|
exp->size = size < 0 ? blk_getlength(blk) : size;
|
|
|
|
if (exp->size < 0) {
|
|
|
|
error_setg_errno(errp, -exp->size,
|
|
|
|
"Failed to determine the NBD export's length");
|
|
|
|
goto fail;
|
|
|
|
}
|
|
|
|
exp->size -= exp->size % BDRV_SECTOR_SIZE;
|
|
|
|
|
2012-09-18 13:59:03 +02:00
|
|
|
exp->close = close;
|
2014-11-18 12:21:18 +01:00
|
|
|
exp->ctx = blk_get_aio_context(blk);
|
|
|
|
blk_add_aio_context_notifier(blk, blk_aio_attached, blk_aio_detach, exp);
|
2016-01-29 16:36:06 +01:00
|
|
|
|
2016-07-06 11:22:39 +02:00
|
|
|
if (on_eject_blk) {
|
|
|
|
blk_ref(on_eject_blk);
|
|
|
|
exp->eject_notifier_blk = on_eject_blk;
|
|
|
|
exp->eject_notifier.notify = nbd_eject_notifier;
|
|
|
|
blk_add_remove_bs_notifier(on_eject_blk, &exp->eject_notifier);
|
|
|
|
}
|
2011-09-19 14:03:37 +02:00
|
|
|
return exp;
|
2015-02-25 19:08:21 +01:00
|
|
|
|
|
|
|
fail:
|
2016-07-06 11:22:39 +02:00
|
|
|
blk_unref(blk);
|
2015-02-25 19:08:21 +01:00
|
|
|
g_free(exp);
|
|
|
|
return NULL;
|
2011-09-19 14:03:37 +02:00
|
|
|
}
|
|
|
|
|
2012-08-22 15:59:23 +02:00
|
|
|
NBDExport *nbd_export_find(const char *name)
|
|
|
|
{
|
|
|
|
NBDExport *exp;
|
|
|
|
QTAILQ_FOREACH(exp, &exports, next) {
|
|
|
|
if (strcmp(name, exp->name) == 0) {
|
|
|
|
return exp;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
return NULL;
|
|
|
|
}
|
|
|
|
|
|
|
|
void nbd_export_set_name(NBDExport *exp, const char *name)
|
|
|
|
{
|
|
|
|
if (exp->name == name) {
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
nbd_export_get(exp);
|
|
|
|
if (exp->name != NULL) {
|
|
|
|
g_free(exp->name);
|
|
|
|
exp->name = NULL;
|
|
|
|
QTAILQ_REMOVE(&exports, exp, next);
|
|
|
|
nbd_export_put(exp);
|
|
|
|
}
|
|
|
|
if (name != NULL) {
|
|
|
|
nbd_export_get(exp);
|
|
|
|
exp->name = g_strdup(name);
|
|
|
|
QTAILQ_INSERT_TAIL(&exports, exp, next);
|
|
|
|
}
|
|
|
|
nbd_export_put(exp);
|
|
|
|
}
|
|
|
|
|
2016-10-14 20:33:03 +02:00
|
|
|
void nbd_export_set_description(NBDExport *exp, const char *description)
|
|
|
|
{
|
|
|
|
g_free(exp->description);
|
|
|
|
exp->description = g_strdup(description);
|
|
|
|
}
|
|
|
|
|
2011-09-19 14:03:37 +02:00
|
|
|
void nbd_export_close(NBDExport *exp)
|
|
|
|
{
|
2012-09-18 13:58:25 +02:00
|
|
|
NBDClient *client, *next;
|
2012-09-18 13:26:25 +02:00
|
|
|
|
2012-09-18 13:58:25 +02:00
|
|
|
nbd_export_get(exp);
|
|
|
|
QTAILQ_FOREACH_SAFE(client, &exp->clients, next, next) {
|
nbd: Fix regression on resiliency to port scan
Back in qemu 2.5, qemu-nbd was immune to port probes (a transient
server would not quit, regardless of how many probe connections
came and went, until a connection actually negotiated). But we
broke that in commit ee7d7aa when removing the return value to
nbd_client_new(), although that patch also introduced a bug causing
an assertion failure on a client that fails negotiation. We then
made it worse during refactoring in commit 1a6245a (a segfault
before we could even assert); the (masked) assertion was cleaned
up in d3780c2 (still in 2.6), and just recently we finally fixed
the segfault ("nbd: Fully intialize client in case of failed
negotiation"). But that still means that ever since we added
TLS support to qemu-nbd, we have been vulnerable to an ill-timed
port-scan being able to cause a denial of service by taking down
qemu-nbd before a real client has a chance to connect.
Since negotiation is now handled asynchronously via coroutines,
we no longer have a synchronous point of return by re-adding a
return value to nbd_client_new(). So this patch instead wires
things up to pass the negotiation status through the close_fn
callback function.
Simple test across two terminals:
$ qemu-nbd -f raw -p 30001 file
$ nmap 127.0.0.1 -p 30001 && \
qemu-io -c 'r 0 512' -f raw nbd://localhost:30001
Note that this patch does not change what constitutes successful
negotiation (thus, a client must enter transmission phase before
that client can be considered as a reason to terminate the server
when the connection ends). Perhaps we may want to tweak things
in a later patch to also treat a client that uses NBD_OPT_ABORT
as being a 'successful' negotiation (the client correctly talked
the NBD protocol, and informed us it was not going to use our
export after all), but that's a discussion for another day.
Fixes: https://bugzilla.redhat.com/show_bug.cgi?id=1451614
Signed-off-by: Eric Blake <eblake@redhat.com>
Message-Id: <20170608222617.20376-1-eblake@redhat.com>
Signed-off-by: Paolo Bonzini <pbonzini@redhat.com>
2017-06-09 00:26:17 +02:00
|
|
|
client_close(client, true);
|
2012-09-18 13:58:25 +02:00
|
|
|
}
|
2012-09-18 14:31:44 +02:00
|
|
|
nbd_export_set_name(exp, NULL);
|
2016-10-14 20:33:03 +02:00
|
|
|
nbd_export_set_description(exp, NULL);
|
2012-09-18 13:58:25 +02:00
|
|
|
nbd_export_put(exp);
|
2012-09-18 13:26:25 +02:00
|
|
|
}
|
|
|
|
|
2018-01-19 14:57:16 +01:00
|
|
|
void nbd_export_remove(NBDExport *exp, NbdServerRemoveMode mode, Error **errp)
|
|
|
|
{
|
|
|
|
if (mode == NBD_SERVER_REMOVE_MODE_HARD || QTAILQ_EMPTY(&exp->clients)) {
|
|
|
|
nbd_export_close(exp);
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
assert(mode == NBD_SERVER_REMOVE_MODE_SAFE);
|
|
|
|
|
|
|
|
error_setg(errp, "export '%s' still in use", exp->name);
|
|
|
|
error_append_hint(errp, "Use mode='hard' to force client disconnect\n");
|
|
|
|
}
|
|
|
|
|
2012-09-18 13:26:25 +02:00
|
|
|
void nbd_export_get(NBDExport *exp)
|
|
|
|
{
|
|
|
|
assert(exp->refcount > 0);
|
|
|
|
exp->refcount++;
|
|
|
|
}
|
|
|
|
|
|
|
|
void nbd_export_put(NBDExport *exp)
|
|
|
|
{
|
|
|
|
assert(exp->refcount > 0);
|
|
|
|
if (exp->refcount == 1) {
|
|
|
|
nbd_export_close(exp);
|
2011-09-19 14:18:33 +02:00
|
|
|
}
|
|
|
|
|
2017-12-07 16:50:57 +01:00
|
|
|
/* nbd_export_close() may theoretically reduce refcount to 0. It may happen
|
|
|
|
* if someone calls nbd_export_put() on named export not through
|
|
|
|
* nbd_export_set_name() when refcount is 1. So, let's assert that
|
|
|
|
* it is > 0.
|
|
|
|
*/
|
|
|
|
assert(exp->refcount > 0);
|
2012-09-18 13:26:25 +02:00
|
|
|
if (--exp->refcount == 0) {
|
2012-08-22 15:59:23 +02:00
|
|
|
assert(exp->name == NULL);
|
2016-10-14 20:33:03 +02:00
|
|
|
assert(exp->description == NULL);
|
2012-08-22 15:59:23 +02:00
|
|
|
|
2012-09-18 13:59:03 +02:00
|
|
|
if (exp->close) {
|
|
|
|
exp->close(exp);
|
|
|
|
}
|
|
|
|
|
2015-09-16 10:35:46 +02:00
|
|
|
if (exp->blk) {
|
2016-07-06 11:22:39 +02:00
|
|
|
if (exp->eject_notifier_blk) {
|
|
|
|
notifier_remove(&exp->eject_notifier);
|
|
|
|
blk_unref(exp->eject_notifier_blk);
|
|
|
|
}
|
2015-09-16 10:35:46 +02:00
|
|
|
blk_remove_aio_context_notifier(exp->blk, blk_aio_attached,
|
|
|
|
blk_aio_detach, exp);
|
|
|
|
blk_unref(exp->blk);
|
|
|
|
exp->blk = NULL;
|
|
|
|
}
|
|
|
|
|
2012-09-18 13:26:25 +02:00
|
|
|
g_free(exp);
|
|
|
|
}
|
2011-09-19 14:03:37 +02:00
|
|
|
}
|
|
|
|
|
2014-11-18 12:21:17 +01:00
|
|
|
BlockBackend *nbd_export_get_blockdev(NBDExport *exp)
|
2012-09-18 14:31:44 +02:00
|
|
|
{
|
2014-11-18 12:21:18 +01:00
|
|
|
return exp->blk;
|
2012-09-18 14:31:44 +02:00
|
|
|
}
|
|
|
|
|
2012-08-22 15:59:23 +02:00
|
|
|
void nbd_export_close_all(void)
|
|
|
|
{
|
|
|
|
NBDExport *exp, *next;
|
|
|
|
|
|
|
|
QTAILQ_FOREACH_SAFE(exp, &exports, next, next) {
|
|
|
|
nbd_export_close(exp);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2017-10-13 00:29:00 +02:00
|
|
|
static int coroutine_fn nbd_co_send_iov(NBDClient *client, struct iovec *iov,
|
|
|
|
unsigned niov, Error **errp)
|
|
|
|
{
|
|
|
|
int ret;
|
|
|
|
|
|
|
|
g_assert(qemu_in_coroutine());
|
|
|
|
qemu_co_mutex_lock(&client->send_lock);
|
|
|
|
client->send_coroutine = qemu_coroutine_self();
|
|
|
|
|
|
|
|
ret = qio_channel_writev_all(client->ioc, iov, niov, errp) < 0 ? -EIO : 0;
|
|
|
|
|
|
|
|
client->send_coroutine = NULL;
|
|
|
|
qemu_co_mutex_unlock(&client->send_lock);
|
|
|
|
|
|
|
|
return ret;
|
|
|
|
}
|
|
|
|
|
2017-10-12 11:53:10 +02:00
|
|
|
static inline void set_be_simple_reply(NBDSimpleReply *reply, uint64_t error,
|
|
|
|
uint64_t handle)
|
|
|
|
{
|
|
|
|
stl_be_p(&reply->magic, NBD_SIMPLE_REPLY_MAGIC);
|
|
|
|
stl_be_p(&reply->error, error);
|
|
|
|
stq_be_p(&reply->handle, handle);
|
|
|
|
}
|
|
|
|
|
2017-10-12 11:53:12 +02:00
|
|
|
static int nbd_co_send_simple_reply(NBDClient *client,
|
2017-10-13 00:05:06 +02:00
|
|
|
uint64_t handle,
|
|
|
|
uint32_t error,
|
2017-10-12 11:53:12 +02:00
|
|
|
void *data,
|
|
|
|
size_t len,
|
|
|
|
Error **errp)
|
2011-09-19 14:25:30 +02:00
|
|
|
{
|
2017-10-13 00:29:00 +02:00
|
|
|
NBDSimpleReply reply;
|
2017-10-13 00:05:06 +02:00
|
|
|
int nbd_err = system_errno_to_nbd_errno(error);
|
2017-10-13 00:29:00 +02:00
|
|
|
struct iovec iov[] = {
|
|
|
|
{.iov_base = &reply, .iov_len = sizeof(reply)},
|
|
|
|
{.iov_base = data, .iov_len = len}
|
|
|
|
};
|
2017-07-07 17:29:17 +02:00
|
|
|
|
2017-10-27 12:40:26 +02:00
|
|
|
trace_nbd_co_send_simple_reply(handle, nbd_err, nbd_err_lookup(nbd_err),
|
|
|
|
len);
|
2017-10-13 00:29:00 +02:00
|
|
|
set_be_simple_reply(&reply, nbd_err, handle);
|
2011-09-19 15:19:27 +02:00
|
|
|
|
2017-10-13 00:29:00 +02:00
|
|
|
return nbd_co_send_iov(client, iov, len ? 2 : 1, errp);
|
2011-09-19 14:25:30 +02:00
|
|
|
}
|
|
|
|
|
2017-10-27 12:40:32 +02:00
|
|
|
static inline void set_be_chunk(NBDStructuredReplyChunk *chunk, uint16_t flags,
|
|
|
|
uint16_t type, uint64_t handle, uint32_t length)
|
|
|
|
{
|
|
|
|
stl_be_p(&chunk->magic, NBD_STRUCTURED_REPLY_MAGIC);
|
|
|
|
stw_be_p(&chunk->flags, flags);
|
|
|
|
stw_be_p(&chunk->type, type);
|
|
|
|
stq_be_p(&chunk->handle, handle);
|
|
|
|
stl_be_p(&chunk->length, length);
|
|
|
|
}
|
|
|
|
|
2017-11-08 22:57:03 +01:00
|
|
|
static int coroutine_fn nbd_co_send_structured_done(NBDClient *client,
|
|
|
|
uint64_t handle,
|
|
|
|
Error **errp)
|
|
|
|
{
|
|
|
|
NBDStructuredReplyChunk chunk;
|
|
|
|
struct iovec iov[] = {
|
|
|
|
{.iov_base = &chunk, .iov_len = sizeof(chunk)},
|
|
|
|
};
|
|
|
|
|
|
|
|
trace_nbd_co_send_structured_done(handle);
|
|
|
|
set_be_chunk(&chunk, NBD_REPLY_FLAG_DONE, NBD_REPLY_TYPE_NONE, handle, 0);
|
|
|
|
|
|
|
|
return nbd_co_send_iov(client, iov, 1, errp);
|
|
|
|
}
|
|
|
|
|
2017-10-27 12:40:32 +02:00
|
|
|
static int coroutine_fn nbd_co_send_structured_read(NBDClient *client,
|
|
|
|
uint64_t handle,
|
|
|
|
uint64_t offset,
|
|
|
|
void *data,
|
|
|
|
size_t size,
|
nbd/server: Implement sparse reads atop structured reply
The reason that NBD added structured reply in the first place was
to allow for efficient reads of sparse files, by allowing the
reply to include chunks to quickly communicate holes to the client
without sending lots of zeroes over the wire. Time to implement
this in the server; our client can already read such data.
We can only skip holes insofar as the block layer can query them;
and only if the client is okay with a fragmented request (if a
client requests NBD_CMD_FLAG_DF and the entire read is a hole, we
could technically return a single NBD_REPLY_TYPE_OFFSET_HOLE, but
that's a fringe case not worth catering to here). Sadly, the
control flow is a bit wonkier than I would have preferred, but
it was minimally invasive to have a split in the action between
a fragmented read (handled directly where we recognize
NBD_CMD_READ with the right conditions, and sending multiple
chunks) vs. a single read (handled at the end of nbd_trip, for
both simple and structured replies, when we know there is only
one thing being read). Likewise, I didn't make any effort to
optimize the final chunk of a fragmented read to set the
NBD_REPLY_FLAG_DONE, but unconditionally send that as a separate
NBD_REPLY_TYPE_NONE.
Signed-off-by: Eric Blake <eblake@redhat.com>
Message-Id: <20171107030912.23930-2-eblake@redhat.com>
Reviewed-by: Vladimir Sementsov-Ogievskiy <vsementsov@virtuozzo.com>
2017-11-07 04:09:11 +01:00
|
|
|
bool final,
|
2017-10-27 12:40:32 +02:00
|
|
|
Error **errp)
|
|
|
|
{
|
2017-11-08 22:57:00 +01:00
|
|
|
NBDStructuredReadData chunk;
|
2017-10-27 12:40:32 +02:00
|
|
|
struct iovec iov[] = {
|
|
|
|
{.iov_base = &chunk, .iov_len = sizeof(chunk)},
|
|
|
|
{.iov_base = data, .iov_len = size}
|
|
|
|
};
|
|
|
|
|
2017-11-08 22:57:03 +01:00
|
|
|
assert(size);
|
2017-10-27 12:40:32 +02:00
|
|
|
trace_nbd_co_send_structured_read(handle, offset, data, size);
|
nbd/server: Implement sparse reads atop structured reply
The reason that NBD added structured reply in the first place was
to allow for efficient reads of sparse files, by allowing the
reply to include chunks to quickly communicate holes to the client
without sending lots of zeroes over the wire. Time to implement
this in the server; our client can already read such data.
We can only skip holes insofar as the block layer can query them;
and only if the client is okay with a fragmented request (if a
client requests NBD_CMD_FLAG_DF and the entire read is a hole, we
could technically return a single NBD_REPLY_TYPE_OFFSET_HOLE, but
that's a fringe case not worth catering to here). Sadly, the
control flow is a bit wonkier than I would have preferred, but
it was minimally invasive to have a split in the action between
a fragmented read (handled directly where we recognize
NBD_CMD_READ with the right conditions, and sending multiple
chunks) vs. a single read (handled at the end of nbd_trip, for
both simple and structured replies, when we know there is only
one thing being read). Likewise, I didn't make any effort to
optimize the final chunk of a fragmented read to set the
NBD_REPLY_FLAG_DONE, but unconditionally send that as a separate
NBD_REPLY_TYPE_NONE.
Signed-off-by: Eric Blake <eblake@redhat.com>
Message-Id: <20171107030912.23930-2-eblake@redhat.com>
Reviewed-by: Vladimir Sementsov-Ogievskiy <vsementsov@virtuozzo.com>
2017-11-07 04:09:11 +01:00
|
|
|
set_be_chunk(&chunk.h, final ? NBD_REPLY_FLAG_DONE : 0,
|
|
|
|
NBD_REPLY_TYPE_OFFSET_DATA, handle,
|
|
|
|
sizeof(chunk) - sizeof(chunk.h) + size);
|
2017-10-27 12:40:32 +02:00
|
|
|
stq_be_p(&chunk.offset, offset);
|
|
|
|
|
|
|
|
return nbd_co_send_iov(client, iov, 2, errp);
|
|
|
|
}
|
|
|
|
|
2018-03-08 19:46:32 +01:00
|
|
|
static int coroutine_fn nbd_co_send_structured_error(NBDClient *client,
|
|
|
|
uint64_t handle,
|
|
|
|
uint32_t error,
|
|
|
|
const char *msg,
|
|
|
|
Error **errp)
|
|
|
|
{
|
|
|
|
NBDStructuredError chunk;
|
|
|
|
int nbd_err = system_errno_to_nbd_errno(error);
|
|
|
|
struct iovec iov[] = {
|
|
|
|
{.iov_base = &chunk, .iov_len = sizeof(chunk)},
|
|
|
|
{.iov_base = (char *)msg, .iov_len = msg ? strlen(msg) : 0},
|
|
|
|
};
|
|
|
|
|
|
|
|
assert(nbd_err);
|
|
|
|
trace_nbd_co_send_structured_error(handle, nbd_err,
|
|
|
|
nbd_err_lookup(nbd_err), msg ? msg : "");
|
|
|
|
set_be_chunk(&chunk.h, NBD_REPLY_FLAG_DONE, NBD_REPLY_TYPE_ERROR, handle,
|
|
|
|
sizeof(chunk) - sizeof(chunk.h) + iov[1].iov_len);
|
|
|
|
stl_be_p(&chunk.error, nbd_err);
|
|
|
|
stw_be_p(&chunk.message_length, iov[1].iov_len);
|
|
|
|
|
|
|
|
return nbd_co_send_iov(client, iov, 1 + !!iov[1].iov_len, errp);
|
|
|
|
}
|
|
|
|
|
2018-03-08 19:46:33 +01:00
|
|
|
/* Do a sparse read and send the structured reply to the client.
|
|
|
|
* Returns -errno if sending fails. bdrv_block_status_above() failure is
|
|
|
|
* reported to the client, at which point this function succeeds.
|
|
|
|
*/
|
nbd/server: Implement sparse reads atop structured reply
The reason that NBD added structured reply in the first place was
to allow for efficient reads of sparse files, by allowing the
reply to include chunks to quickly communicate holes to the client
without sending lots of zeroes over the wire. Time to implement
this in the server; our client can already read such data.
We can only skip holes insofar as the block layer can query them;
and only if the client is okay with a fragmented request (if a
client requests NBD_CMD_FLAG_DF and the entire read is a hole, we
could technically return a single NBD_REPLY_TYPE_OFFSET_HOLE, but
that's a fringe case not worth catering to here). Sadly, the
control flow is a bit wonkier than I would have preferred, but
it was minimally invasive to have a split in the action between
a fragmented read (handled directly where we recognize
NBD_CMD_READ with the right conditions, and sending multiple
chunks) vs. a single read (handled at the end of nbd_trip, for
both simple and structured replies, when we know there is only
one thing being read). Likewise, I didn't make any effort to
optimize the final chunk of a fragmented read to set the
NBD_REPLY_FLAG_DONE, but unconditionally send that as a separate
NBD_REPLY_TYPE_NONE.
Signed-off-by: Eric Blake <eblake@redhat.com>
Message-Id: <20171107030912.23930-2-eblake@redhat.com>
Reviewed-by: Vladimir Sementsov-Ogievskiy <vsementsov@virtuozzo.com>
2017-11-07 04:09:11 +01:00
|
|
|
static int coroutine_fn nbd_co_send_sparse_read(NBDClient *client,
|
|
|
|
uint64_t handle,
|
|
|
|
uint64_t offset,
|
|
|
|
uint8_t *data,
|
|
|
|
size_t size,
|
|
|
|
Error **errp)
|
|
|
|
{
|
|
|
|
int ret = 0;
|
|
|
|
NBDExport *exp = client->exp;
|
|
|
|
size_t progress = 0;
|
|
|
|
|
|
|
|
while (progress < size) {
|
|
|
|
int64_t pnum;
|
|
|
|
int status = bdrv_block_status_above(blk_bs(exp->blk), NULL,
|
|
|
|
offset + progress,
|
|
|
|
size - progress, &pnum, NULL,
|
|
|
|
NULL);
|
2017-11-07 04:09:12 +01:00
|
|
|
bool final;
|
nbd/server: Implement sparse reads atop structured reply
The reason that NBD added structured reply in the first place was
to allow for efficient reads of sparse files, by allowing the
reply to include chunks to quickly communicate holes to the client
without sending lots of zeroes over the wire. Time to implement
this in the server; our client can already read such data.
We can only skip holes insofar as the block layer can query them;
and only if the client is okay with a fragmented request (if a
client requests NBD_CMD_FLAG_DF and the entire read is a hole, we
could technically return a single NBD_REPLY_TYPE_OFFSET_HOLE, but
that's a fringe case not worth catering to here). Sadly, the
control flow is a bit wonkier than I would have preferred, but
it was minimally invasive to have a split in the action between
a fragmented read (handled directly where we recognize
NBD_CMD_READ with the right conditions, and sending multiple
chunks) vs. a single read (handled at the end of nbd_trip, for
both simple and structured replies, when we know there is only
one thing being read). Likewise, I didn't make any effort to
optimize the final chunk of a fragmented read to set the
NBD_REPLY_FLAG_DONE, but unconditionally send that as a separate
NBD_REPLY_TYPE_NONE.
Signed-off-by: Eric Blake <eblake@redhat.com>
Message-Id: <20171107030912.23930-2-eblake@redhat.com>
Reviewed-by: Vladimir Sementsov-Ogievskiy <vsementsov@virtuozzo.com>
2017-11-07 04:09:11 +01:00
|
|
|
|
|
|
|
if (status < 0) {
|
2018-03-08 19:46:33 +01:00
|
|
|
char *msg = g_strdup_printf("unable to check for holes: %s",
|
|
|
|
strerror(-status));
|
|
|
|
|
|
|
|
ret = nbd_co_send_structured_error(client, handle, -status, msg,
|
|
|
|
errp);
|
|
|
|
g_free(msg);
|
|
|
|
return ret;
|
nbd/server: Implement sparse reads atop structured reply
The reason that NBD added structured reply in the first place was
to allow for efficient reads of sparse files, by allowing the
reply to include chunks to quickly communicate holes to the client
without sending lots of zeroes over the wire. Time to implement
this in the server; our client can already read such data.
We can only skip holes insofar as the block layer can query them;
and only if the client is okay with a fragmented request (if a
client requests NBD_CMD_FLAG_DF and the entire read is a hole, we
could technically return a single NBD_REPLY_TYPE_OFFSET_HOLE, but
that's a fringe case not worth catering to here). Sadly, the
control flow is a bit wonkier than I would have preferred, but
it was minimally invasive to have a split in the action between
a fragmented read (handled directly where we recognize
NBD_CMD_READ with the right conditions, and sending multiple
chunks) vs. a single read (handled at the end of nbd_trip, for
both simple and structured replies, when we know there is only
one thing being read). Likewise, I didn't make any effort to
optimize the final chunk of a fragmented read to set the
NBD_REPLY_FLAG_DONE, but unconditionally send that as a separate
NBD_REPLY_TYPE_NONE.
Signed-off-by: Eric Blake <eblake@redhat.com>
Message-Id: <20171107030912.23930-2-eblake@redhat.com>
Reviewed-by: Vladimir Sementsov-Ogievskiy <vsementsov@virtuozzo.com>
2017-11-07 04:09:11 +01:00
|
|
|
}
|
|
|
|
assert(pnum && pnum <= size - progress);
|
2017-11-07 04:09:12 +01:00
|
|
|
final = progress + pnum == size;
|
nbd/server: Implement sparse reads atop structured reply
The reason that NBD added structured reply in the first place was
to allow for efficient reads of sparse files, by allowing the
reply to include chunks to quickly communicate holes to the client
without sending lots of zeroes over the wire. Time to implement
this in the server; our client can already read such data.
We can only skip holes insofar as the block layer can query them;
and only if the client is okay with a fragmented request (if a
client requests NBD_CMD_FLAG_DF and the entire read is a hole, we
could technically return a single NBD_REPLY_TYPE_OFFSET_HOLE, but
that's a fringe case not worth catering to here). Sadly, the
control flow is a bit wonkier than I would have preferred, but
it was minimally invasive to have a split in the action between
a fragmented read (handled directly where we recognize
NBD_CMD_READ with the right conditions, and sending multiple
chunks) vs. a single read (handled at the end of nbd_trip, for
both simple and structured replies, when we know there is only
one thing being read). Likewise, I didn't make any effort to
optimize the final chunk of a fragmented read to set the
NBD_REPLY_FLAG_DONE, but unconditionally send that as a separate
NBD_REPLY_TYPE_NONE.
Signed-off-by: Eric Blake <eblake@redhat.com>
Message-Id: <20171107030912.23930-2-eblake@redhat.com>
Reviewed-by: Vladimir Sementsov-Ogievskiy <vsementsov@virtuozzo.com>
2017-11-07 04:09:11 +01:00
|
|
|
if (status & BDRV_BLOCK_ZERO) {
|
|
|
|
NBDStructuredReadHole chunk;
|
|
|
|
struct iovec iov[] = {
|
|
|
|
{.iov_base = &chunk, .iov_len = sizeof(chunk)},
|
|
|
|
};
|
|
|
|
|
|
|
|
trace_nbd_co_send_structured_read_hole(handle, offset + progress,
|
|
|
|
pnum);
|
2017-11-07 04:09:12 +01:00
|
|
|
set_be_chunk(&chunk.h, final ? NBD_REPLY_FLAG_DONE : 0,
|
|
|
|
NBD_REPLY_TYPE_OFFSET_HOLE,
|
nbd/server: Implement sparse reads atop structured reply
The reason that NBD added structured reply in the first place was
to allow for efficient reads of sparse files, by allowing the
reply to include chunks to quickly communicate holes to the client
without sending lots of zeroes over the wire. Time to implement
this in the server; our client can already read such data.
We can only skip holes insofar as the block layer can query them;
and only if the client is okay with a fragmented request (if a
client requests NBD_CMD_FLAG_DF and the entire read is a hole, we
could technically return a single NBD_REPLY_TYPE_OFFSET_HOLE, but
that's a fringe case not worth catering to here). Sadly, the
control flow is a bit wonkier than I would have preferred, but
it was minimally invasive to have a split in the action between
a fragmented read (handled directly where we recognize
NBD_CMD_READ with the right conditions, and sending multiple
chunks) vs. a single read (handled at the end of nbd_trip, for
both simple and structured replies, when we know there is only
one thing being read). Likewise, I didn't make any effort to
optimize the final chunk of a fragmented read to set the
NBD_REPLY_FLAG_DONE, but unconditionally send that as a separate
NBD_REPLY_TYPE_NONE.
Signed-off-by: Eric Blake <eblake@redhat.com>
Message-Id: <20171107030912.23930-2-eblake@redhat.com>
Reviewed-by: Vladimir Sementsov-Ogievskiy <vsementsov@virtuozzo.com>
2017-11-07 04:09:11 +01:00
|
|
|
handle, sizeof(chunk) - sizeof(chunk.h));
|
|
|
|
stq_be_p(&chunk.offset, offset + progress);
|
|
|
|
stl_be_p(&chunk.length, pnum);
|
|
|
|
ret = nbd_co_send_iov(client, iov, 1, errp);
|
|
|
|
} else {
|
|
|
|
ret = blk_pread(exp->blk, offset + progress + exp->dev_offset,
|
|
|
|
data + progress, pnum);
|
|
|
|
if (ret < 0) {
|
|
|
|
error_setg_errno(errp, -ret, "reading from file failed");
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
ret = nbd_co_send_structured_read(client, handle, offset + progress,
|
2017-11-07 04:09:12 +01:00
|
|
|
data + progress, pnum, final,
|
nbd/server: Implement sparse reads atop structured reply
The reason that NBD added structured reply in the first place was
to allow for efficient reads of sparse files, by allowing the
reply to include chunks to quickly communicate holes to the client
without sending lots of zeroes over the wire. Time to implement
this in the server; our client can already read such data.
We can only skip holes insofar as the block layer can query them;
and only if the client is okay with a fragmented request (if a
client requests NBD_CMD_FLAG_DF and the entire read is a hole, we
could technically return a single NBD_REPLY_TYPE_OFFSET_HOLE, but
that's a fringe case not worth catering to here). Sadly, the
control flow is a bit wonkier than I would have preferred, but
it was minimally invasive to have a split in the action between
a fragmented read (handled directly where we recognize
NBD_CMD_READ with the right conditions, and sending multiple
chunks) vs. a single read (handled at the end of nbd_trip, for
both simple and structured replies, when we know there is only
one thing being read). Likewise, I didn't make any effort to
optimize the final chunk of a fragmented read to set the
NBD_REPLY_FLAG_DONE, but unconditionally send that as a separate
NBD_REPLY_TYPE_NONE.
Signed-off-by: Eric Blake <eblake@redhat.com>
Message-Id: <20171107030912.23930-2-eblake@redhat.com>
Reviewed-by: Vladimir Sementsov-Ogievskiy <vsementsov@virtuozzo.com>
2017-11-07 04:09:11 +01:00
|
|
|
errp);
|
|
|
|
}
|
|
|
|
|
|
|
|
if (ret < 0) {
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
progress += pnum;
|
|
|
|
}
|
|
|
|
return ret;
|
|
|
|
}
|
|
|
|
|
2018-03-12 16:21:21 +01:00
|
|
|
static int blockstatus_to_extent_be(BlockDriverState *bs, uint64_t offset,
|
|
|
|
uint64_t bytes, NBDExtent *extent)
|
|
|
|
{
|
|
|
|
uint64_t remaining_bytes = bytes;
|
|
|
|
|
|
|
|
while (remaining_bytes) {
|
|
|
|
uint32_t flags;
|
|
|
|
int64_t num;
|
|
|
|
int ret = bdrv_block_status_above(bs, NULL, offset, remaining_bytes,
|
|
|
|
&num, NULL, NULL);
|
|
|
|
if (ret < 0) {
|
|
|
|
return ret;
|
|
|
|
}
|
|
|
|
|
|
|
|
flags = (ret & BDRV_BLOCK_ALLOCATED ? 0 : NBD_STATE_HOLE) |
|
|
|
|
(ret & BDRV_BLOCK_ZERO ? NBD_STATE_ZERO : 0);
|
|
|
|
|
|
|
|
if (remaining_bytes == bytes) {
|
|
|
|
extent->flags = flags;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (flags != extent->flags) {
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
|
|
|
offset += num;
|
|
|
|
remaining_bytes -= num;
|
|
|
|
}
|
|
|
|
|
|
|
|
cpu_to_be32s(&extent->flags);
|
|
|
|
extent->length = cpu_to_be32(bytes - remaining_bytes);
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* nbd_co_send_extents
|
|
|
|
* @extents should be in big-endian */
|
|
|
|
static int nbd_co_send_extents(NBDClient *client, uint64_t handle,
|
|
|
|
NBDExtent *extents, unsigned nb_extents,
|
|
|
|
uint32_t context_id, Error **errp)
|
|
|
|
{
|
|
|
|
NBDStructuredMeta chunk;
|
|
|
|
|
|
|
|
struct iovec iov[] = {
|
|
|
|
{.iov_base = &chunk, .iov_len = sizeof(chunk)},
|
|
|
|
{.iov_base = extents, .iov_len = nb_extents * sizeof(extents[0])}
|
|
|
|
};
|
|
|
|
|
|
|
|
set_be_chunk(&chunk.h, NBD_REPLY_FLAG_DONE, NBD_REPLY_TYPE_BLOCK_STATUS,
|
|
|
|
handle, sizeof(chunk) - sizeof(chunk.h) + iov[1].iov_len);
|
|
|
|
stl_be_p(&chunk.context_id, context_id);
|
|
|
|
|
|
|
|
return nbd_co_send_iov(client, iov, 2, errp);
|
|
|
|
}
|
|
|
|
|
|
|
|
/* Get block status from the exported device and send it to the client */
|
|
|
|
static int nbd_co_send_block_status(NBDClient *client, uint64_t handle,
|
|
|
|
BlockDriverState *bs, uint64_t offset,
|
|
|
|
uint64_t length, uint32_t context_id,
|
|
|
|
Error **errp)
|
|
|
|
{
|
|
|
|
int ret;
|
|
|
|
NBDExtent extent;
|
|
|
|
|
|
|
|
ret = blockstatus_to_extent_be(bs, offset, length, &extent);
|
|
|
|
if (ret < 0) {
|
|
|
|
return nbd_co_send_structured_error(
|
|
|
|
client, handle, -ret, "can't get block status", errp);
|
|
|
|
}
|
|
|
|
|
|
|
|
return nbd_co_send_extents(client, handle, &extent, 1, context_id, errp);
|
|
|
|
}
|
|
|
|
|
2017-06-02 17:01:44 +02:00
|
|
|
/* nbd_co_receive_request
|
|
|
|
* Collect a client request. Return 0 if request looks valid, -EIO to drop
|
|
|
|
* connection right away, and any other negative value to report an error to
|
|
|
|
* the client (although the caller may still need to disconnect after reporting
|
|
|
|
* the error).
|
|
|
|
*/
|
2017-07-07 17:29:11 +02:00
|
|
|
static int nbd_co_receive_request(NBDRequestData *req, NBDRequest *request,
|
|
|
|
Error **errp)
|
2011-09-19 15:07:54 +02:00
|
|
|
{
|
2011-10-07 16:47:56 +02:00
|
|
|
NBDClient *client = req->client;
|
2017-10-27 12:40:32 +02:00
|
|
|
int valid_flags;
|
2011-09-19 15:07:54 +02:00
|
|
|
|
2016-02-10 19:41:04 +01:00
|
|
|
g_assert(qemu_in_coroutine());
|
2017-02-13 14:52:24 +01:00
|
|
|
assert(client->recv_coroutine == qemu_coroutine_self());
|
2017-07-07 17:29:11 +02:00
|
|
|
if (nbd_receive_request(client->ioc, request, errp) < 0) {
|
2017-06-02 17:01:45 +02:00
|
|
|
return -EIO;
|
2011-09-19 15:07:54 +02:00
|
|
|
}
|
|
|
|
|
2017-07-07 22:30:43 +02:00
|
|
|
trace_nbd_co_receive_request_decode_type(request->handle, request->type,
|
|
|
|
nbd_cmd_lookup(request->type));
|
2016-05-12 00:39:37 +02:00
|
|
|
|
2016-10-14 20:33:04 +02:00
|
|
|
if (request->type != NBD_CMD_WRITE) {
|
2016-05-12 00:39:37 +02:00
|
|
|
/* No payload, we are ready to read the next request. */
|
|
|
|
req->complete = true;
|
|
|
|
}
|
|
|
|
|
2016-10-14 20:33:04 +02:00
|
|
|
if (request->type == NBD_CMD_DISC) {
|
2016-05-12 00:39:37 +02:00
|
|
|
/* Special case: we're going to disconnect without a reply,
|
|
|
|
* whether or not flags, from, or len are bogus */
|
2017-06-02 17:01:45 +02:00
|
|
|
return -EIO;
|
2016-05-12 00:39:37 +02:00
|
|
|
}
|
|
|
|
|
2016-10-14 20:33:04 +02:00
|
|
|
if (request->type == NBD_CMD_READ || request->type == NBD_CMD_WRITE) {
|
2016-01-07 14:32:42 +01:00
|
|
|
if (request->len > NBD_MAX_BUFFER_SIZE) {
|
2017-07-07 17:29:11 +02:00
|
|
|
error_setg(errp, "len (%" PRIu32" ) is larger than max len (%u)",
|
|
|
|
request->len, NBD_MAX_BUFFER_SIZE);
|
2017-06-02 17:01:45 +02:00
|
|
|
return -EINVAL;
|
2016-01-07 14:32:42 +01:00
|
|
|
}
|
|
|
|
|
2016-01-07 14:34:13 +01:00
|
|
|
req->data = blk_try_blockalign(client->exp->blk, request->len);
|
|
|
|
if (req->data == NULL) {
|
2017-07-07 17:29:11 +02:00
|
|
|
error_setg(errp, "No memory");
|
2017-06-02 17:01:45 +02:00
|
|
|
return -ENOMEM;
|
2016-01-07 14:34:13 +01:00
|
|
|
}
|
2013-05-02 14:23:08 +02:00
|
|
|
}
|
2016-10-14 20:33:04 +02:00
|
|
|
if (request->type == NBD_CMD_WRITE) {
|
2017-07-07 17:29:11 +02:00
|
|
|
if (nbd_read(client->ioc, req->data, request->len, errp) < 0) {
|
|
|
|
error_prepend(errp, "reading from socket failed: ");
|
2017-06-02 17:01:45 +02:00
|
|
|
return -EIO;
|
2011-09-19 15:07:54 +02:00
|
|
|
}
|
2016-05-12 00:39:37 +02:00
|
|
|
req->complete = true;
|
2017-07-07 17:29:17 +02:00
|
|
|
|
2017-07-07 17:29:18 +02:00
|
|
|
trace_nbd_co_receive_request_payload_received(request->handle,
|
|
|
|
request->len);
|
2011-09-19 15:07:54 +02:00
|
|
|
}
|
2016-05-12 00:39:37 +02:00
|
|
|
|
nbd/server: Fix error reporting for bad requests
The NBD spec says an attempt to NBD_CMD_TRIM on a read-only
export should fail with EPERM, as a trim has the potential
to change disk contents, but we were relying on the block
layer to catch that for us, which might not always give the
right error (and even if it does, it does not let us pass
back a sane message for structured replies).
The NBD spec says an attempt to NBD_CMD_WRITE_ZEROES out of
bounds should fail with ENOSPC, not EINVAL.
Our check for u64 offset + u32 length wraparound up front is
pointless; nothing uses offset until after the second round
of sanity checks, and we can just as easily ensure there is
no wraparound by checking whether offset is in bounds (since
a disk size cannot exceed off_t which is 63 bits, adding a
32-bit number for a valid offset can't overflow). Bonus:
dropping the up-front check lets us keep the connection alive
after NBD_CMD_WRITE, whereas before we would drop the
connection (of course, any client sending a packet that would
trigger the failure is already buggy, so it's also okay to
drop the connection, but better quality-of-implementation
never hurts).
Solve all of these issues by some code motion and improved
request validation.
Signed-off-by: Eric Blake <eblake@redhat.com>
Message-Id: <20171115213557.3548-1-eblake@redhat.com>
Reviewed-by: Vladimir Sementsov-Ogievskiy <vsementsov@virtuozzo.com>
2017-11-15 22:35:56 +01:00
|
|
|
/* Sanity checks. */
|
|
|
|
if (client->exp->nbdflags & NBD_FLAG_READ_ONLY &&
|
|
|
|
(request->type == NBD_CMD_WRITE ||
|
|
|
|
request->type == NBD_CMD_WRITE_ZEROES ||
|
|
|
|
request->type == NBD_CMD_TRIM)) {
|
|
|
|
error_setg(errp, "Export is read-only");
|
|
|
|
return -EROFS;
|
|
|
|
}
|
|
|
|
if (request->from > client->exp->size ||
|
|
|
|
request->from + request->len > client->exp->size) {
|
2017-07-07 17:29:11 +02:00
|
|
|
error_setg(errp, "operation past EOF; From: %" PRIu64 ", Len: %" PRIu32
|
|
|
|
", Size: %" PRIu64, request->from, request->len,
|
|
|
|
(uint64_t)client->exp->size);
|
nbd/server: Fix error reporting for bad requests
The NBD spec says an attempt to NBD_CMD_TRIM on a read-only
export should fail with EPERM, as a trim has the potential
to change disk contents, but we were relying on the block
layer to catch that for us, which might not always give the
right error (and even if it does, it does not let us pass
back a sane message for structured replies).
The NBD spec says an attempt to NBD_CMD_WRITE_ZEROES out of
bounds should fail with ENOSPC, not EINVAL.
Our check for u64 offset + u32 length wraparound up front is
pointless; nothing uses offset until after the second round
of sanity checks, and we can just as easily ensure there is
no wraparound by checking whether offset is in bounds (since
a disk size cannot exceed off_t which is 63 bits, adding a
32-bit number for a valid offset can't overflow). Bonus:
dropping the up-front check lets us keep the connection alive
after NBD_CMD_WRITE, whereas before we would drop the
connection (of course, any client sending a packet that would
trigger the failure is already buggy, so it's also okay to
drop the connection, but better quality-of-implementation
never hurts).
Solve all of these issues by some code motion and improved
request validation.
Signed-off-by: Eric Blake <eblake@redhat.com>
Message-Id: <20171115213557.3548-1-eblake@redhat.com>
Reviewed-by: Vladimir Sementsov-Ogievskiy <vsementsov@virtuozzo.com>
2017-11-15 22:35:56 +01:00
|
|
|
return (request->type == NBD_CMD_WRITE ||
|
|
|
|
request->type == NBD_CMD_WRITE_ZEROES) ? -ENOSPC : -EINVAL;
|
2016-05-12 00:39:37 +02:00
|
|
|
}
|
2017-10-27 12:40:32 +02:00
|
|
|
valid_flags = NBD_CMD_FLAG_FUA;
|
|
|
|
if (request->type == NBD_CMD_READ && client->structured_reply) {
|
|
|
|
valid_flags |= NBD_CMD_FLAG_DF;
|
|
|
|
} else if (request->type == NBD_CMD_WRITE_ZEROES) {
|
|
|
|
valid_flags |= NBD_CMD_FLAG_NO_HOLE;
|
2018-03-12 16:21:21 +01:00
|
|
|
} else if (request->type == NBD_CMD_BLOCK_STATUS) {
|
|
|
|
valid_flags |= NBD_CMD_FLAG_REQ_ONE;
|
2016-05-12 00:39:38 +02:00
|
|
|
}
|
2017-10-27 12:40:32 +02:00
|
|
|
if (request->flags & ~valid_flags) {
|
|
|
|
error_setg(errp, "unsupported flags for command %s (got 0x%x)",
|
|
|
|
nbd_cmd_lookup(request->type), request->flags);
|
2017-06-02 17:01:45 +02:00
|
|
|
return -EINVAL;
|
2016-10-14 20:33:17 +02:00
|
|
|
}
|
2016-05-12 00:39:37 +02:00
|
|
|
|
2017-06-02 17:01:45 +02:00
|
|
|
return 0;
|
2011-09-19 15:07:54 +02:00
|
|
|
}
|
|
|
|
|
2018-03-08 19:46:35 +01:00
|
|
|
/* Send simple reply without a payload, or a structured error
|
|
|
|
* @error_msg is ignored if @ret >= 0
|
|
|
|
* Returns 0 if connection is still live, -errno on failure to talk to client
|
|
|
|
*/
|
|
|
|
static coroutine_fn int nbd_send_generic_reply(NBDClient *client,
|
|
|
|
uint64_t handle,
|
|
|
|
int ret,
|
|
|
|
const char *error_msg,
|
|
|
|
Error **errp)
|
|
|
|
{
|
|
|
|
if (client->structured_reply && ret < 0) {
|
|
|
|
return nbd_co_send_structured_error(client, handle, -ret, error_msg,
|
|
|
|
errp);
|
|
|
|
} else {
|
|
|
|
return nbd_co_send_simple_reply(client, handle, ret < 0 ? -ret : 0,
|
|
|
|
NULL, 0, errp);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
/* Handle NBD_CMD_READ request.
|
|
|
|
* Return -errno if sending fails. Other errors are reported directly to the
|
|
|
|
* client as an error reply. */
|
|
|
|
static coroutine_fn int nbd_do_cmd_read(NBDClient *client, NBDRequest *request,
|
|
|
|
uint8_t *data, Error **errp)
|
|
|
|
{
|
|
|
|
int ret;
|
|
|
|
NBDExport *exp = client->exp;
|
|
|
|
|
|
|
|
assert(request->type == NBD_CMD_READ);
|
|
|
|
|
|
|
|
/* XXX: NBD Protocol only documents use of FUA with WRITE */
|
|
|
|
if (request->flags & NBD_CMD_FLAG_FUA) {
|
|
|
|
ret = blk_co_flush(exp->blk);
|
|
|
|
if (ret < 0) {
|
|
|
|
return nbd_send_generic_reply(client, request->handle, ret,
|
|
|
|
"flush failed", errp);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
if (client->structured_reply && !(request->flags & NBD_CMD_FLAG_DF) &&
|
|
|
|
request->len) {
|
|
|
|
return nbd_co_send_sparse_read(client, request->handle, request->from,
|
|
|
|
data, request->len, errp);
|
|
|
|
}
|
|
|
|
|
|
|
|
ret = blk_pread(exp->blk, request->from + exp->dev_offset, data,
|
|
|
|
request->len);
|
|
|
|
if (ret < 0) {
|
|
|
|
return nbd_send_generic_reply(client, request->handle, ret,
|
|
|
|
"reading from file failed", errp);
|
|
|
|
}
|
|
|
|
|
|
|
|
if (client->structured_reply) {
|
|
|
|
if (request->len) {
|
|
|
|
return nbd_co_send_structured_read(client, request->handle,
|
|
|
|
request->from, data,
|
|
|
|
request->len, true, errp);
|
|
|
|
} else {
|
|
|
|
return nbd_co_send_structured_done(client, request->handle, errp);
|
|
|
|
}
|
|
|
|
} else {
|
|
|
|
return nbd_co_send_simple_reply(client, request->handle, 0,
|
|
|
|
data, request->len, errp);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2018-03-12 23:14:28 +01:00
|
|
|
/* Handle NBD request.
|
|
|
|
* Return -errno if sending fails. Other errors are reported directly to the
|
|
|
|
* client as an error reply. */
|
|
|
|
static coroutine_fn int nbd_handle_request(NBDClient *client,
|
|
|
|
NBDRequest *request,
|
|
|
|
uint8_t *data, Error **errp)
|
|
|
|
{
|
|
|
|
int ret;
|
|
|
|
int flags;
|
|
|
|
NBDExport *exp = client->exp;
|
|
|
|
char *msg;
|
|
|
|
|
|
|
|
switch (request->type) {
|
|
|
|
case NBD_CMD_READ:
|
|
|
|
return nbd_do_cmd_read(client, request, data, errp);
|
|
|
|
|
|
|
|
case NBD_CMD_WRITE:
|
|
|
|
flags = 0;
|
|
|
|
if (request->flags & NBD_CMD_FLAG_FUA) {
|
|
|
|
flags |= BDRV_REQ_FUA;
|
|
|
|
}
|
|
|
|
ret = blk_pwrite(exp->blk, request->from + exp->dev_offset,
|
|
|
|
data, request->len, flags);
|
|
|
|
return nbd_send_generic_reply(client, request->handle, ret,
|
|
|
|
"writing to file failed", errp);
|
|
|
|
|
|
|
|
case NBD_CMD_WRITE_ZEROES:
|
|
|
|
flags = 0;
|
|
|
|
if (request->flags & NBD_CMD_FLAG_FUA) {
|
|
|
|
flags |= BDRV_REQ_FUA;
|
|
|
|
}
|
|
|
|
if (!(request->flags & NBD_CMD_FLAG_NO_HOLE)) {
|
|
|
|
flags |= BDRV_REQ_MAY_UNMAP;
|
|
|
|
}
|
|
|
|
ret = blk_pwrite_zeroes(exp->blk, request->from + exp->dev_offset,
|
|
|
|
request->len, flags);
|
|
|
|
return nbd_send_generic_reply(client, request->handle, ret,
|
|
|
|
"writing to file failed", errp);
|
|
|
|
|
|
|
|
case NBD_CMD_DISC:
|
|
|
|
/* unreachable, thanks to special case in nbd_co_receive_request() */
|
|
|
|
abort();
|
|
|
|
|
|
|
|
case NBD_CMD_FLUSH:
|
|
|
|
ret = blk_co_flush(exp->blk);
|
|
|
|
return nbd_send_generic_reply(client, request->handle, ret,
|
|
|
|
"flush failed", errp);
|
|
|
|
|
|
|
|
case NBD_CMD_TRIM:
|
|
|
|
ret = blk_co_pdiscard(exp->blk, request->from + exp->dev_offset,
|
|
|
|
request->len);
|
nbd/server: Honor FUA request on NBD_CMD_TRIM
The NBD spec states that since trim requests can affect disk contents,
then they should allow for FUA semantics just like writes for ensuring
the disk has settled before returning. As bdrv_[co_]pdiscard() does
not support a flags argument, we can't pass FUA down the block layer
stack, and must therefore emulate it with a flush at the NBD layer.
Note that in all reality, generic well-behaved clients will never
send TRIM+FUA (in fact, qemu as a client never does, and we have no
intention to plumb flags into bdrv_pdiscard). This is because the
NBD protocol states that it is unspecified to READ a trimmed area
(you might read stale data, all zeroes, or even random unrelated
data) without first rewriting it, and even the experimental
BLOCK_STATUS extension states that TRIM need not affect reported
status. Thus, in the general case, a client cannot tell the
difference between an arbitrary server that ignores TRIM, a server
that had a power outage without flushing to disk, and a server that
actually affected the disk before returning; so waiting for the
trim actions to flush to disk makes little sense. However, for a
specific client and server pair, where the client knows the server
treats TRIM'd areas as guaranteed reads-zero, waiting for a flush
makes sense, hence why the protocol documents that FUA is valid on
trim. So, even though the NBD protocol doesn't have a way for the
server to advertise what effects (if any) TRIM will actually have,
and thus any client that relies on specific effects is probably
in error, we can at least support a client that requests TRIM+FUA.
Signed-off-by: Eric Blake <eblake@redhat.com>
Message-Id: <20180307225732.155835-1-eblake@redhat.com>
2018-03-07 23:57:32 +01:00
|
|
|
if (ret == 0 && request->flags & NBD_CMD_FLAG_FUA) {
|
|
|
|
ret = blk_co_flush(exp->blk);
|
|
|
|
}
|
2018-03-12 23:14:28 +01:00
|
|
|
return nbd_send_generic_reply(client, request->handle, ret,
|
|
|
|
"discard failed", errp);
|
|
|
|
|
2018-03-12 16:21:21 +01:00
|
|
|
case NBD_CMD_BLOCK_STATUS:
|
|
|
|
if (client->export_meta.valid && client->export_meta.base_allocation) {
|
|
|
|
return nbd_co_send_block_status(client, request->handle,
|
|
|
|
blk_bs(exp->blk), request->from,
|
|
|
|
request->len,
|
|
|
|
NBD_META_ID_BASE_ALLOCATION, errp);
|
|
|
|
} else {
|
|
|
|
return nbd_send_generic_reply(client, request->handle, -EINVAL,
|
|
|
|
"CMD_BLOCK_STATUS not negotiated",
|
|
|
|
errp);
|
|
|
|
}
|
|
|
|
|
2018-03-12 23:14:28 +01:00
|
|
|
default:
|
|
|
|
msg = g_strdup_printf("invalid request type (%" PRIu32 ") received",
|
|
|
|
request->type);
|
|
|
|
ret = nbd_send_generic_reply(client, request->handle, -EINVAL, msg,
|
|
|
|
errp);
|
|
|
|
g_free(msg);
|
|
|
|
return ret;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2017-02-13 14:52:24 +01:00
|
|
|
/* Owns a reference to the NBDClient passed as opaque. */
|
|
|
|
static coroutine_fn void nbd_trip(void *opaque)
|
2008-07-03 15:41:03 +02:00
|
|
|
{
|
2011-09-19 15:19:27 +02:00
|
|
|
NBDClient *client = opaque;
|
2016-10-14 20:33:05 +02:00
|
|
|
NBDRequestData *req;
|
2017-02-13 14:52:24 +01:00
|
|
|
NBDRequest request = { 0 }; /* GCC thinks it can be used uninitialized */
|
2017-06-02 17:01:42 +02:00
|
|
|
int ret;
|
2017-07-07 17:29:11 +02:00
|
|
|
Error *local_err = NULL;
|
2011-02-22 16:44:51 +01:00
|
|
|
|
2017-07-07 17:29:18 +02:00
|
|
|
trace_nbd_trip();
|
2012-08-22 18:45:12 +02:00
|
|
|
if (client->closing) {
|
2017-02-13 14:52:24 +01:00
|
|
|
nbd_client_put(client);
|
2012-08-22 18:45:12 +02:00
|
|
|
return;
|
|
|
|
}
|
2011-02-22 16:44:51 +01:00
|
|
|
|
2012-08-22 18:45:12 +02:00
|
|
|
req = nbd_request_get(client);
|
2017-07-07 17:29:11 +02:00
|
|
|
ret = nbd_co_receive_request(req, &request, &local_err);
|
2017-06-02 17:01:45 +02:00
|
|
|
client->recv_coroutine = NULL;
|
2011-02-22 16:44:51 +01:00
|
|
|
|
2015-09-16 10:35:46 +02:00
|
|
|
if (client->closing) {
|
|
|
|
/*
|
|
|
|
* The client may be closed when we are blocked in
|
|
|
|
* nbd_co_receive_request()
|
|
|
|
*/
|
|
|
|
goto done;
|
|
|
|
}
|
|
|
|
|
nbd/server: fix: check client->closing before sending reply
Since the unchanged code has just set client->recv_coroutine to
NULL before calling nbd_client_receive_next_request(), we are
spawning a new coroutine unconditionally, but the first thing
that coroutine will do is check for client->closing, making it
a no-op if we have already detected that the client is going
away. Furthermore, for any error other than EIO (where we
disconnect, which itself sets client->closing), if the client
has already gone away, we'll probably encounter EIO later
in the function and attempt disconnect at that point. Logically,
as soon as we know the connection is closing, there is no need
to try a likely-to-fail a response or spawn a no-op coroutine.
Signed-off-by: Vladimir Sementsov-Ogievskiy <vsementsov@virtuozzo.com>
Message-Id: <20180308184636.178534-4-vsementsov@virtuozzo.com>
Reviewed-by: Eric Blake <eblake@redhat.com>
[eblake: squash in further reordering: hoist check before spawning
next coroutine, and document rationale in commit message]
Signed-off-by: Eric Blake <eblake@redhat.com>
2018-03-08 19:46:34 +01:00
|
|
|
nbd_client_receive_next_request(client);
|
|
|
|
if (ret == -EIO) {
|
|
|
|
goto disconnect;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (ret < 0) {
|
2018-03-08 19:46:35 +01:00
|
|
|
/* It wans't -EIO, so, according to nbd_co_receive_request()
|
|
|
|
* semantics, we should return the error to the client. */
|
|
|
|
Error *export_err = local_err;
|
|
|
|
|
|
|
|
local_err = NULL;
|
|
|
|
ret = nbd_send_generic_reply(client, request.handle, -EINVAL,
|
|
|
|
error_get_pretty(export_err), &local_err);
|
|
|
|
error_free(export_err);
|
2018-03-12 23:14:28 +01:00
|
|
|
} else {
|
|
|
|
ret = nbd_handle_request(client, &request, req->data, &local_err);
|
2017-10-27 12:40:32 +02:00
|
|
|
}
|
|
|
|
if (ret < 0) {
|
2017-07-07 17:29:12 +02:00
|
|
|
error_prepend(&local_err, "Failed to send reply: ");
|
2017-07-07 17:29:11 +02:00
|
|
|
goto disconnect;
|
|
|
|
}
|
|
|
|
|
2017-06-02 17:01:50 +02:00
|
|
|
/* We must disconnect after NBD_CMD_WRITE if we did not
|
|
|
|
* read the payload.
|
|
|
|
*/
|
2017-07-07 17:29:11 +02:00
|
|
|
if (!req->complete) {
|
|
|
|
error_setg(&local_err, "Request handling failed in intermediate state");
|
2017-06-02 17:01:50 +02:00
|
|
|
goto disconnect;
|
2011-02-22 16:44:51 +01:00
|
|
|
}
|
|
|
|
|
2012-03-05 09:10:35 +01:00
|
|
|
done:
|
2011-09-19 15:19:27 +02:00
|
|
|
nbd_request_put(req);
|
2017-02-13 14:52:24 +01:00
|
|
|
nbd_client_put(client);
|
2011-09-19 15:19:27 +02:00
|
|
|
return;
|
|
|
|
|
2017-06-02 17:01:50 +02:00
|
|
|
disconnect:
|
2017-07-07 17:29:11 +02:00
|
|
|
if (local_err) {
|
|
|
|
error_reportf_err(local_err, "Disconnect client, due to: ");
|
|
|
|
}
|
2011-10-07 16:47:56 +02:00
|
|
|
nbd_request_put(req);
|
nbd: Fix regression on resiliency to port scan
Back in qemu 2.5, qemu-nbd was immune to port probes (a transient
server would not quit, regardless of how many probe connections
came and went, until a connection actually negotiated). But we
broke that in commit ee7d7aa when removing the return value to
nbd_client_new(), although that patch also introduced a bug causing
an assertion failure on a client that fails negotiation. We then
made it worse during refactoring in commit 1a6245a (a segfault
before we could even assert); the (masked) assertion was cleaned
up in d3780c2 (still in 2.6), and just recently we finally fixed
the segfault ("nbd: Fully intialize client in case of failed
negotiation"). But that still means that ever since we added
TLS support to qemu-nbd, we have been vulnerable to an ill-timed
port-scan being able to cause a denial of service by taking down
qemu-nbd before a real client has a chance to connect.
Since negotiation is now handled asynchronously via coroutines,
we no longer have a synchronous point of return by re-adding a
return value to nbd_client_new(). So this patch instead wires
things up to pass the negotiation status through the close_fn
callback function.
Simple test across two terminals:
$ qemu-nbd -f raw -p 30001 file
$ nmap 127.0.0.1 -p 30001 && \
qemu-io -c 'r 0 512' -f raw nbd://localhost:30001
Note that this patch does not change what constitutes successful
negotiation (thus, a client must enter transmission phase before
that client can be considered as a reason to terminate the server
when the connection ends). Perhaps we may want to tweak things
in a later patch to also treat a client that uses NBD_OPT_ABORT
as being a 'successful' negotiation (the client correctly talked
the NBD protocol, and informed us it was not going to use our
export after all), but that's a discussion for another day.
Fixes: https://bugzilla.redhat.com/show_bug.cgi?id=1451614
Signed-off-by: Eric Blake <eblake@redhat.com>
Message-Id: <20170608222617.20376-1-eblake@redhat.com>
Signed-off-by: Paolo Bonzini <pbonzini@redhat.com>
2017-06-09 00:26:17 +02:00
|
|
|
client_close(client, true);
|
2017-02-13 14:52:24 +01:00
|
|
|
nbd_client_put(client);
|
2008-05-27 23:13:40 +02:00
|
|
|
}
|
2011-09-19 14:03:37 +02:00
|
|
|
|
2017-02-13 14:52:24 +01:00
|
|
|
static void nbd_client_receive_next_request(NBDClient *client)
|
2014-06-20 21:57:32 +02:00
|
|
|
{
|
2017-02-13 14:52:24 +01:00
|
|
|
if (!client->recv_coroutine && client->nb_requests < MAX_NBD_REQUESTS) {
|
|
|
|
nbd_client_get(client);
|
|
|
|
client->recv_coroutine = qemu_coroutine_create(nbd_trip, client);
|
|
|
|
aio_co_schedule(client->exp->ctx, client->recv_coroutine);
|
2014-06-20 21:57:32 +02:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2016-01-14 09:41:03 +01:00
|
|
|
static coroutine_fn void nbd_co_client_start(void *opaque)
|
|
|
|
{
|
2017-06-02 17:01:46 +02:00
|
|
|
NBDClient *client = opaque;
|
2016-01-14 09:41:03 +01:00
|
|
|
NBDExport *exp = client->exp;
|
2017-07-07 17:29:11 +02:00
|
|
|
Error *local_err = NULL;
|
2016-01-14 09:41:03 +01:00
|
|
|
|
|
|
|
if (exp) {
|
|
|
|
nbd_export_get(exp);
|
2017-05-27 05:04:21 +02:00
|
|
|
QTAILQ_INSERT_TAIL(&exp->clients, client, next);
|
2016-01-14 09:41:03 +01:00
|
|
|
}
|
2017-05-27 05:04:21 +02:00
|
|
|
qemu_co_mutex_init(&client->send_lock);
|
|
|
|
|
2017-07-07 17:29:11 +02:00
|
|
|
if (nbd_negotiate(client, &local_err)) {
|
|
|
|
if (local_err) {
|
|
|
|
error_report_err(local_err);
|
|
|
|
}
|
nbd: Fix regression on resiliency to port scan
Back in qemu 2.5, qemu-nbd was immune to port probes (a transient
server would not quit, regardless of how many probe connections
came and went, until a connection actually negotiated). But we
broke that in commit ee7d7aa when removing the return value to
nbd_client_new(), although that patch also introduced a bug causing
an assertion failure on a client that fails negotiation. We then
made it worse during refactoring in commit 1a6245a (a segfault
before we could even assert); the (masked) assertion was cleaned
up in d3780c2 (still in 2.6), and just recently we finally fixed
the segfault ("nbd: Fully intialize client in case of failed
negotiation"). But that still means that ever since we added
TLS support to qemu-nbd, we have been vulnerable to an ill-timed
port-scan being able to cause a denial of service by taking down
qemu-nbd before a real client has a chance to connect.
Since negotiation is now handled asynchronously via coroutines,
we no longer have a synchronous point of return by re-adding a
return value to nbd_client_new(). So this patch instead wires
things up to pass the negotiation status through the close_fn
callback function.
Simple test across two terminals:
$ qemu-nbd -f raw -p 30001 file
$ nmap 127.0.0.1 -p 30001 && \
qemu-io -c 'r 0 512' -f raw nbd://localhost:30001
Note that this patch does not change what constitutes successful
negotiation (thus, a client must enter transmission phase before
that client can be considered as a reason to terminate the server
when the connection ends). Perhaps we may want to tweak things
in a later patch to also treat a client that uses NBD_OPT_ABORT
as being a 'successful' negotiation (the client correctly talked
the NBD protocol, and informed us it was not going to use our
export after all), but that's a discussion for another day.
Fixes: https://bugzilla.redhat.com/show_bug.cgi?id=1451614
Signed-off-by: Eric Blake <eblake@redhat.com>
Message-Id: <20170608222617.20376-1-eblake@redhat.com>
Signed-off-by: Paolo Bonzini <pbonzini@redhat.com>
2017-06-09 00:26:17 +02:00
|
|
|
client_close(client, false);
|
2017-06-02 17:01:46 +02:00
|
|
|
return;
|
2016-01-14 09:41:03 +01:00
|
|
|
}
|
2017-02-13 14:52:24 +01:00
|
|
|
|
|
|
|
nbd_client_receive_next_request(client);
|
2016-01-14 09:41:03 +01:00
|
|
|
}
|
|
|
|
|
nbd: Fix regression on resiliency to port scan
Back in qemu 2.5, qemu-nbd was immune to port probes (a transient
server would not quit, regardless of how many probe connections
came and went, until a connection actually negotiated). But we
broke that in commit ee7d7aa when removing the return value to
nbd_client_new(), although that patch also introduced a bug causing
an assertion failure on a client that fails negotiation. We then
made it worse during refactoring in commit 1a6245a (a segfault
before we could even assert); the (masked) assertion was cleaned
up in d3780c2 (still in 2.6), and just recently we finally fixed
the segfault ("nbd: Fully intialize client in case of failed
negotiation"). But that still means that ever since we added
TLS support to qemu-nbd, we have been vulnerable to an ill-timed
port-scan being able to cause a denial of service by taking down
qemu-nbd before a real client has a chance to connect.
Since negotiation is now handled asynchronously via coroutines,
we no longer have a synchronous point of return by re-adding a
return value to nbd_client_new(). So this patch instead wires
things up to pass the negotiation status through the close_fn
callback function.
Simple test across two terminals:
$ qemu-nbd -f raw -p 30001 file
$ nmap 127.0.0.1 -p 30001 && \
qemu-io -c 'r 0 512' -f raw nbd://localhost:30001
Note that this patch does not change what constitutes successful
negotiation (thus, a client must enter transmission phase before
that client can be considered as a reason to terminate the server
when the connection ends). Perhaps we may want to tweak things
in a later patch to also treat a client that uses NBD_OPT_ABORT
as being a 'successful' negotiation (the client correctly talked
the NBD protocol, and informed us it was not going to use our
export after all), but that's a discussion for another day.
Fixes: https://bugzilla.redhat.com/show_bug.cgi?id=1451614
Signed-off-by: Eric Blake <eblake@redhat.com>
Message-Id: <20170608222617.20376-1-eblake@redhat.com>
Signed-off-by: Paolo Bonzini <pbonzini@redhat.com>
2017-06-09 00:26:17 +02:00
|
|
|
/*
|
|
|
|
* Create a new client listener on the given export @exp, using the
|
|
|
|
* given channel @sioc. Begin servicing it in a coroutine. When the
|
|
|
|
* connection closes, call @close_fn with an indication of whether the
|
|
|
|
* client completed negotiation.
|
|
|
|
*/
|
2016-02-10 19:41:04 +01:00
|
|
|
void nbd_client_new(NBDExport *exp,
|
|
|
|
QIOChannelSocket *sioc,
|
2016-02-10 19:41:11 +01:00
|
|
|
QCryptoTLSCreds *tlscreds,
|
|
|
|
const char *tlsaclname,
|
nbd: Fix regression on resiliency to port scan
Back in qemu 2.5, qemu-nbd was immune to port probes (a transient
server would not quit, regardless of how many probe connections
came and went, until a connection actually negotiated). But we
broke that in commit ee7d7aa when removing the return value to
nbd_client_new(), although that patch also introduced a bug causing
an assertion failure on a client that fails negotiation. We then
made it worse during refactoring in commit 1a6245a (a segfault
before we could even assert); the (masked) assertion was cleaned
up in d3780c2 (still in 2.6), and just recently we finally fixed
the segfault ("nbd: Fully intialize client in case of failed
negotiation"). But that still means that ever since we added
TLS support to qemu-nbd, we have been vulnerable to an ill-timed
port-scan being able to cause a denial of service by taking down
qemu-nbd before a real client has a chance to connect.
Since negotiation is now handled asynchronously via coroutines,
we no longer have a synchronous point of return by re-adding a
return value to nbd_client_new(). So this patch instead wires
things up to pass the negotiation status through the close_fn
callback function.
Simple test across two terminals:
$ qemu-nbd -f raw -p 30001 file
$ nmap 127.0.0.1 -p 30001 && \
qemu-io -c 'r 0 512' -f raw nbd://localhost:30001
Note that this patch does not change what constitutes successful
negotiation (thus, a client must enter transmission phase before
that client can be considered as a reason to terminate the server
when the connection ends). Perhaps we may want to tweak things
in a later patch to also treat a client that uses NBD_OPT_ABORT
as being a 'successful' negotiation (the client correctly talked
the NBD protocol, and informed us it was not going to use our
export after all), but that's a discussion for another day.
Fixes: https://bugzilla.redhat.com/show_bug.cgi?id=1451614
Signed-off-by: Eric Blake <eblake@redhat.com>
Message-Id: <20170608222617.20376-1-eblake@redhat.com>
Signed-off-by: Paolo Bonzini <pbonzini@redhat.com>
2017-06-09 00:26:17 +02:00
|
|
|
void (*close_fn)(NBDClient *, bool))
|
2011-09-19 14:03:37 +02:00
|
|
|
{
|
2011-09-19 14:33:23 +02:00
|
|
|
NBDClient *client;
|
2017-06-02 17:01:46 +02:00
|
|
|
Coroutine *co;
|
2016-01-14 09:41:03 +01:00
|
|
|
|
2017-10-07 01:49:16 +02:00
|
|
|
client = g_new0(NBDClient, 1);
|
2011-09-19 14:33:23 +02:00
|
|
|
client->refcount = 1;
|
|
|
|
client->exp = exp;
|
2016-02-10 19:41:11 +01:00
|
|
|
client->tlscreds = tlscreds;
|
|
|
|
if (tlscreds) {
|
|
|
|
object_ref(OBJECT(client->tlscreds));
|
|
|
|
}
|
|
|
|
client->tlsaclname = g_strdup(tlsaclname);
|
2016-02-10 19:41:04 +01:00
|
|
|
client->sioc = sioc;
|
|
|
|
object_ref(OBJECT(client->sioc));
|
|
|
|
client->ioc = QIO_CHANNEL(sioc);
|
|
|
|
object_ref(OBJECT(client->ioc));
|
nbd: Fix regression on resiliency to port scan
Back in qemu 2.5, qemu-nbd was immune to port probes (a transient
server would not quit, regardless of how many probe connections
came and went, until a connection actually negotiated). But we
broke that in commit ee7d7aa when removing the return value to
nbd_client_new(), although that patch also introduced a bug causing
an assertion failure on a client that fails negotiation. We then
made it worse during refactoring in commit 1a6245a (a segfault
before we could even assert); the (masked) assertion was cleaned
up in d3780c2 (still in 2.6), and just recently we finally fixed
the segfault ("nbd: Fully intialize client in case of failed
negotiation"). But that still means that ever since we added
TLS support to qemu-nbd, we have been vulnerable to an ill-timed
port-scan being able to cause a denial of service by taking down
qemu-nbd before a real client has a chance to connect.
Since negotiation is now handled asynchronously via coroutines,
we no longer have a synchronous point of return by re-adding a
return value to nbd_client_new(). So this patch instead wires
things up to pass the negotiation status through the close_fn
callback function.
Simple test across two terminals:
$ qemu-nbd -f raw -p 30001 file
$ nmap 127.0.0.1 -p 30001 && \
qemu-io -c 'r 0 512' -f raw nbd://localhost:30001
Note that this patch does not change what constitutes successful
negotiation (thus, a client must enter transmission phase before
that client can be considered as a reason to terminate the server
when the connection ends). Perhaps we may want to tweak things
in a later patch to also treat a client that uses NBD_OPT_ABORT
as being a 'successful' negotiation (the client correctly talked
the NBD protocol, and informed us it was not going to use our
export after all), but that's a discussion for another day.
Fixes: https://bugzilla.redhat.com/show_bug.cgi?id=1451614
Signed-off-by: Eric Blake <eblake@redhat.com>
Message-Id: <20170608222617.20376-1-eblake@redhat.com>
Signed-off-by: Paolo Bonzini <pbonzini@redhat.com>
2017-06-09 00:26:17 +02:00
|
|
|
client->close_fn = close_fn;
|
2012-09-18 13:26:25 +02:00
|
|
|
|
2017-06-02 17:01:46 +02:00
|
|
|
co = qemu_coroutine_create(nbd_co_client_start, client);
|
|
|
|
qemu_coroutine_enter(co);
|
2011-09-19 14:03:37 +02:00
|
|
|
}
|