2012-08-22 18:43:07 +04:00
|
|
|
/*
|
|
|
|
* Serving QEMU block devices via NBD
|
|
|
|
*
|
|
|
|
* Copyright (c) 2012 Red Hat, Inc.
|
|
|
|
*
|
|
|
|
* Author: Paolo Bonzini <pbonzini@redhat.com>
|
|
|
|
*
|
|
|
|
* This work is licensed under the terms of the GNU GPL, version 2 or
|
|
|
|
* later. See the COPYING file in the top-level directory.
|
|
|
|
*/
|
|
|
|
|
2016-01-29 20:50:05 +03:00
|
|
|
#include "qemu/osdep.h"
|
2012-12-17 21:20:04 +04:00
|
|
|
#include "sysemu/blockdev.h"
|
2014-11-18 14:21:17 +03:00
|
|
|
#include "sysemu/block-backend.h"
|
2013-02-05 20:06:20 +04:00
|
|
|
#include "hw/block/block.h"
|
2018-02-01 14:18:31 +03:00
|
|
|
#include "qapi/error.h"
|
2018-02-11 12:36:01 +03:00
|
|
|
#include "qapi/qapi-commands-block.h"
|
2012-12-17 21:20:04 +04:00
|
|
|
#include "sysemu/sysemu.h"
|
2012-12-17 21:19:44 +04:00
|
|
|
#include "block/nbd.h"
|
2016-02-10 21:41:03 +03:00
|
|
|
#include "io/channel-socket.h"
|
2017-12-18 13:16:42 +03:00
|
|
|
#include "io/net-listener.h"
|
2012-08-22 18:43:07 +04:00
|
|
|
|
2016-02-10 21:41:14 +03:00
|
|
|
typedef struct NBDServerData {
|
2017-12-18 13:16:42 +03:00
|
|
|
QIONetListener *listener;
|
2016-02-10 21:41:14 +03:00
|
|
|
QCryptoTLSCreds *tlscreds;
|
|
|
|
} NBDServerData;
|
|
|
|
|
|
|
|
static NBDServerData *nbd_server;
|
|
|
|
|
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 01:26:17 +03:00
|
|
|
static void nbd_blockdev_client_closed(NBDClient *client, bool ignored)
|
|
|
|
{
|
|
|
|
nbd_client_put(client);
|
|
|
|
}
|
2012-08-22 18:43:07 +04:00
|
|
|
|
2017-12-18 13:16:42 +03:00
|
|
|
static void nbd_accept(QIONetListener *listener, QIOChannelSocket *cioc,
|
|
|
|
gpointer opaque)
|
2012-08-22 18:43:07 +04:00
|
|
|
{
|
2016-09-30 13:57:14 +03:00
|
|
|
qio_channel_set_name(QIO_CHANNEL(cioc), "nbd-server");
|
2018-10-03 20:02:28 +03:00
|
|
|
nbd_client_new(cioc, nbd_server->tlscreds, NULL,
|
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 01:26:17 +03:00
|
|
|
nbd_blockdev_client_closed);
|
2012-08-22 18:43:07 +04:00
|
|
|
}
|
|
|
|
|
2016-02-10 21:41:14 +03:00
|
|
|
|
|
|
|
static void nbd_server_free(NBDServerData *server)
|
2012-08-22 18:43:07 +04:00
|
|
|
{
|
2016-02-10 21:41:14 +03:00
|
|
|
if (!server) {
|
2012-08-22 18:43:07 +04:00
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
2017-12-18 13:16:42 +03:00
|
|
|
qio_net_listener_disconnect(server->listener);
|
|
|
|
object_unref(OBJECT(server->listener));
|
2016-02-10 21:41:14 +03:00
|
|
|
if (server->tlscreds) {
|
|
|
|
object_unref(OBJECT(server->tlscreds));
|
|
|
|
}
|
|
|
|
|
|
|
|
g_free(server);
|
|
|
|
}
|
|
|
|
|
|
|
|
static QCryptoTLSCreds *nbd_get_tls_creds(const char *id, Error **errp)
|
|
|
|
{
|
|
|
|
Object *obj;
|
|
|
|
QCryptoTLSCreds *creds;
|
|
|
|
|
|
|
|
obj = object_resolve_path_component(
|
|
|
|
object_get_objects_root(), id);
|
|
|
|
if (!obj) {
|
|
|
|
error_setg(errp, "No TLS credentials with id '%s'",
|
|
|
|
id);
|
|
|
|
return NULL;
|
|
|
|
}
|
|
|
|
creds = (QCryptoTLSCreds *)
|
|
|
|
object_dynamic_cast(obj, TYPE_QCRYPTO_TLS_CREDS);
|
|
|
|
if (!creds) {
|
|
|
|
error_setg(errp, "Object with id '%s' is not TLS credentials",
|
|
|
|
id);
|
|
|
|
return NULL;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (creds->endpoint != QCRYPTO_TLS_CREDS_ENDPOINT_SERVER) {
|
|
|
|
error_setg(errp,
|
|
|
|
"Expecting TLS credentials with a server endpoint");
|
|
|
|
return NULL;
|
|
|
|
}
|
|
|
|
object_ref(obj);
|
|
|
|
return creds;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2017-04-26 10:36:41 +03:00
|
|
|
void nbd_server_start(SocketAddress *addr, const char *tls_creds,
|
|
|
|
Error **errp)
|
2016-02-10 21:41:14 +03:00
|
|
|
{
|
|
|
|
if (nbd_server) {
|
|
|
|
error_setg(errp, "NBD server already running");
|
2016-02-10 21:41:03 +03:00
|
|
|
return;
|
2012-08-22 18:43:07 +04:00
|
|
|
}
|
2016-02-10 21:41:03 +03:00
|
|
|
|
2016-02-10 21:41:14 +03:00
|
|
|
nbd_server = g_new0(NBDServerData, 1);
|
2017-12-18 13:16:42 +03:00
|
|
|
nbd_server->listener = qio_net_listener_new();
|
|
|
|
|
|
|
|
qio_net_listener_set_name(nbd_server->listener,
|
|
|
|
"nbd-listener");
|
|
|
|
|
|
|
|
if (qio_net_listener_open_sync(nbd_server->listener, addr, errp) < 0) {
|
2016-02-10 21:41:14 +03:00
|
|
|
goto error;
|
|
|
|
}
|
|
|
|
|
2017-04-26 10:36:41 +03:00
|
|
|
if (tls_creds) {
|
2016-02-10 21:41:14 +03:00
|
|
|
nbd_server->tlscreds = nbd_get_tls_creds(tls_creds, errp);
|
|
|
|
if (!nbd_server->tlscreds) {
|
|
|
|
goto error;
|
|
|
|
}
|
|
|
|
|
2017-04-26 10:36:41 +03:00
|
|
|
/* TODO SOCKET_ADDRESS_TYPE_FD where fd has AF_INET or AF_INET6 */
|
|
|
|
if (addr->type != SOCKET_ADDRESS_TYPE_INET) {
|
2016-02-10 21:41:14 +03:00
|
|
|
error_setg(errp, "TLS is only supported with IPv4/IPv6");
|
|
|
|
goto error;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2017-12-18 13:16:42 +03:00
|
|
|
qio_net_listener_set_client_func(nbd_server->listener,
|
|
|
|
nbd_accept,
|
|
|
|
NULL,
|
|
|
|
NULL);
|
2016-02-10 21:41:14 +03:00
|
|
|
|
|
|
|
return;
|
|
|
|
|
|
|
|
error:
|
|
|
|
nbd_server_free(nbd_server);
|
|
|
|
nbd_server = NULL;
|
2012-08-22 18:43:07 +04:00
|
|
|
}
|
|
|
|
|
2017-04-26 10:36:41 +03:00
|
|
|
void qmp_nbd_server_start(SocketAddressLegacy *addr,
|
|
|
|
bool has_tls_creds, const char *tls_creds,
|
|
|
|
Error **errp)
|
|
|
|
{
|
|
|
|
SocketAddress *addr_flat = socket_address_flatten(addr);
|
|
|
|
|
|
|
|
nbd_server_start(addr_flat, tls_creds, errp);
|
|
|
|
qapi_free_SocketAddress(addr_flat);
|
|
|
|
}
|
|
|
|
|
2018-01-19 16:57:15 +03:00
|
|
|
void qmp_nbd_server_add(const char *device, bool has_name, const char *name,
|
nbd: Allow bitmap export during QMP nbd-server-add
With the experimental x-nbd-server-add-bitmap command, there was
a window of time where an NBD client could see the export but not
the associated dirty bitmap, which can cause a client that planned
on using the dirty bitmap to be forced to treat the entire image
as dirty as a safety fallback. Furthermore, if the QMP client
successfully exports a disk but then fails to add the bitmap, it
has to take on the burden of removing the export. Since we don't
allow changing the exposed dirty bitmap (whether to a different
bitmap, or removing advertisement of the bitmap), it is nicer to
make the bitmap tied to the export at the time the export is
created, with automatic failure to export if the bitmap is not
available.
The experimental command included an optional 'bitmap-export-name'
field for remapping the name exposed over NBD to be different from
the bitmap name stored on disk. However, my libvirt demo code
for implementing differential backups on top of persistent bitmaps
did not need to take advantage of that feature (it is instead
possible to create a new temporary bitmap with the desired name,
use block-dirty-bitmap-merge to merge one or more persistent
bitmaps into the temporary, then associate the temporary with the
NBD export, if control is needed over the exported bitmap name).
Hence, I'm not copying that part of the experiment over to the
stable addition. For more details on the libvirt demo, see
https://www.redhat.com/archives/libvir-list/2018-October/msg01254.html,
https://kvmforum2018.sched.com/event/FzuB/facilitating-incremental-backup-eric-blake-red-hat
This patch focuses on the user interface, and reduces (but does
not completely eliminate) the window where an NBD client can see
the export but not the dirty bitmap, with less work to clean up
after errors. Later patches will add further cleanups now that
this interface is declared stable via a single QMP command,
including removing the race window.
Update test 223 to use the new interface.
Signed-off-by: Eric Blake <eblake@redhat.com>
Reviewed-by: Vladimir Sementsov-Ogievskiy <vsementsov@virtuozzo.com>
Message-Id: <20190111194720.15671-6-eblake@redhat.com>
2019-01-11 22:47:17 +03:00
|
|
|
bool has_writable, bool writable,
|
|
|
|
bool has_bitmap, const char *bitmap, Error **errp)
|
2012-08-22 18:43:07 +04:00
|
|
|
{
|
2016-07-06 15:15:51 +03:00
|
|
|
BlockDriverState *bs = NULL;
|
|
|
|
BlockBackend *on_eject_blk;
|
2012-08-22 18:43:07 +04:00
|
|
|
NBDExport *exp;
|
|
|
|
|
2016-02-10 21:41:14 +03:00
|
|
|
if (!nbd_server) {
|
2012-11-12 17:25:17 +04:00
|
|
|
error_setg(errp, "NBD server not running");
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
2018-01-19 16:57:15 +03:00
|
|
|
if (!has_name) {
|
|
|
|
name = device;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (nbd_export_find(name)) {
|
|
|
|
error_setg(errp, "NBD server already has export named '%s'", name);
|
2012-08-22 18:43:07 +04:00
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
2016-07-06 15:15:51 +03:00
|
|
|
on_eject_blk = blk_by_name(device);
|
|
|
|
|
|
|
|
bs = bdrv_lookup_bs(device, device, errp);
|
|
|
|
if (!bs) {
|
2014-05-18 14:50:04 +04:00
|
|
|
return;
|
|
|
|
}
|
2012-08-22 18:43:07 +04:00
|
|
|
|
2012-11-04 15:43:37 +04:00
|
|
|
if (!has_writable) {
|
2012-11-28 14:46:39 +04:00
|
|
|
writable = false;
|
2012-11-04 15:43:37 +04:00
|
|
|
}
|
2016-07-06 15:15:51 +03:00
|
|
|
if (bdrv_is_read_only(bs)) {
|
2012-11-04 15:43:37 +04:00
|
|
|
writable = false;
|
|
|
|
}
|
|
|
|
|
2019-01-11 22:47:19 +03:00
|
|
|
exp = nbd_export_new(bs, 0, -1, name, NULL, bitmap,
|
nbd: Merge nbd_export_set_name into nbd_export_new
The existing NBD code had a weird split where nbd_export_new()
created an export but did not add it to the list of exported
names until a later nbd_export_set_name() came along and grabbed
a second reference on the object; later, the first call to
nbd_export_close() drops the second reference while removing
the export from the list. This is in part because the QAPI
NbdServerRemoveNode enum documents the possibility of adding a
mode where we could do a soft disconnect: preventing new clients,
but waiting for existing clients to gracefully quit, based on
the mode used when calling nbd_export_close().
But in spite of all that, note that we never change the name of
an NBD export while it is exposed, which means it is easier to
just inline the process of setting the name as part of creating
the export.
Inline the contents of nbd_export_set_name() and
nbd_export_set_description() into the two points in an export
lifecycle where they matter, then adjust both callers to pass
the name up front. Note that for creation, all callers pass a
non-NULL name, (passing NULL at creation was for old style
servers, but we removed support for that in commit 7f7dfe2a),
so we can add an assert and do things unconditionally; but for
cleanup, because of the dual nature of nbd_export_close(), we
still have to be careful to avoid use-after-free. Along the
way, add a comment reminding ourselves of the potential of
adding a middle mode disconnect.
Signed-off-by: Eric Blake <eblake@redhat.com>
Reviewed-by: Vladimir Sementsov-Ogievskiy <vsementsov@virtuozzo.com>
Message-Id: <20190111194720.15671-5-eblake@redhat.com>
2019-01-11 22:47:16 +03:00
|
|
|
writable ? 0 : NBD_FLAG_READ_ONLY,
|
2016-07-06 15:15:51 +03:00
|
|
|
NULL, false, on_eject_blk, errp);
|
2015-02-25 21:08:21 +03:00
|
|
|
if (!exp) {
|
|
|
|
return;
|
|
|
|
}
|
2012-08-22 18:43:07 +04:00
|
|
|
|
2016-01-29 18:36:06 +03:00
|
|
|
/* The list of named exports has a strong reference to this export now and
|
|
|
|
* our only way of accessing it is through nbd_export_find(), so we can drop
|
|
|
|
* the strong reference that is @exp. */
|
|
|
|
nbd_export_put(exp);
|
2012-08-22 18:43:07 +04:00
|
|
|
}
|
|
|
|
|
2018-01-19 16:57:16 +03:00
|
|
|
void qmp_nbd_server_remove(const char *name,
|
|
|
|
bool has_mode, NbdServerRemoveMode mode,
|
|
|
|
Error **errp)
|
|
|
|
{
|
|
|
|
NBDExport *exp;
|
|
|
|
|
|
|
|
if (!nbd_server) {
|
|
|
|
error_setg(errp, "NBD server not running");
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
exp = nbd_export_find(name);
|
|
|
|
if (exp == NULL) {
|
|
|
|
error_setg(errp, "Export '%s' is not found", name);
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (!has_mode) {
|
|
|
|
mode = NBD_SERVER_REMOVE_MODE_SAFE;
|
|
|
|
}
|
|
|
|
|
|
|
|
nbd_export_remove(exp, mode, errp);
|
|
|
|
}
|
|
|
|
|
2012-08-22 18:43:07 +04:00
|
|
|
void qmp_nbd_server_stop(Error **errp)
|
|
|
|
{
|
2019-01-11 22:47:14 +03:00
|
|
|
if (!nbd_server) {
|
|
|
|
error_setg(errp, "NBD server not running");
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
2016-01-29 18:36:06 +03:00
|
|
|
nbd_export_close_all();
|
2012-08-22 18:43:07 +04:00
|
|
|
|
2016-02-10 21:41:14 +03:00
|
|
|
nbd_server_free(nbd_server);
|
|
|
|
nbd_server = NULL;
|
2012-08-22 18:43:07 +04:00
|
|
|
}
|