xref: /qemu/blockdev-nbd.c (revision 7b3b616838c30830c004b746e7b1209297118318)
16dd844dbSPaolo Bonzini /*
26dd844dbSPaolo Bonzini  * Serving QEMU block devices via NBD
36dd844dbSPaolo Bonzini  *
46dd844dbSPaolo Bonzini  * Copyright (c) 2012 Red Hat, Inc.
56dd844dbSPaolo Bonzini  *
66dd844dbSPaolo Bonzini  * Author: Paolo Bonzini <pbonzini@redhat.com>
76dd844dbSPaolo Bonzini  *
86dd844dbSPaolo Bonzini  * This work is licensed under the terms of the GNU GPL, version 2 or
96dd844dbSPaolo Bonzini  * later.  See the COPYING file in the top-level directory.
106dd844dbSPaolo Bonzini  */
116dd844dbSPaolo Bonzini 
12d38ea87aSPeter Maydell #include "qemu/osdep.h"
139c17d615SPaolo Bonzini #include "sysemu/blockdev.h"
14e140177dSMax Reitz #include "sysemu/block-backend.h"
150d09e41aSPaolo Bonzini #include "hw/block/block.h"
16e688df6bSMarkus Armbruster #include "qapi/error.h"
178675cbd6SEric Blake #include "qapi/clone-visitor.h"
188675cbd6SEric Blake #include "qapi/qapi-visit-block-export.h"
195daa6bfdSKevin Wolf #include "qapi/qapi-commands-block-export.h"
20737e150eSPaolo Bonzini #include "block/nbd.h"
21ae398278SDaniel P. Berrange #include "io/channel-socket.h"
22862172f4SDaniel P. Berrange #include "io/net-listener.h"
236dd844dbSPaolo Bonzini 
24ddffee39SDaniel P. Berrange typedef struct NBDServerData {
25862172f4SDaniel P. Berrange     QIONetListener *listener;
26ddffee39SDaniel P. Berrange     QCryptoTLSCreds *tlscreds;
2700019455SDaniel P. Berrange     char *tlsauthz;
281c8222b0SKevin Wolf     uint32_t max_connections;
291c8222b0SKevin Wolf     uint32_t connections;
30ddffee39SDaniel P. Berrange } NBDServerData;
31ddffee39SDaniel P. Berrange 
32ddffee39SDaniel P. Berrange static NBDServerData *nbd_server;
3300917172SKevin Wolf static bool is_qemu_nbd;
34ddffee39SDaniel P. Berrange 
351c8222b0SKevin Wolf static void nbd_update_server_watch(NBDServerData *s);
361c8222b0SKevin Wolf 
3700917172SKevin Wolf void nbd_server_is_qemu_nbd(bool value)
3800917172SKevin Wolf {
3900917172SKevin Wolf     is_qemu_nbd = value;
4000917172SKevin Wolf }
4100917172SKevin Wolf 
425b1cb497SKevin Wolf bool nbd_server_is_running(void)
435b1cb497SKevin Wolf {
445b1cb497SKevin Wolf     return nbd_server || is_qemu_nbd;
455b1cb497SKevin Wolf }
465b1cb497SKevin Wolf 
470c9390d9SEric Blake static void nbd_blockdev_client_closed(NBDClient *client, bool ignored)
480c9390d9SEric Blake {
490c9390d9SEric Blake     nbd_client_put(client);
501c8222b0SKevin Wolf     assert(nbd_server->connections > 0);
511c8222b0SKevin Wolf     nbd_server->connections--;
521c8222b0SKevin Wolf     nbd_update_server_watch(nbd_server);
530c9390d9SEric Blake }
546dd844dbSPaolo Bonzini 
55862172f4SDaniel P. Berrange static void nbd_accept(QIONetListener *listener, QIOChannelSocket *cioc,
56ae398278SDaniel P. Berrange                        gpointer opaque)
576dd844dbSPaolo Bonzini {
581c8222b0SKevin Wolf     nbd_server->connections++;
591c8222b0SKevin Wolf     nbd_update_server_watch(nbd_server);
601c8222b0SKevin Wolf 
610d73f725SDaniel P. Berrange     qio_channel_set_name(QIO_CHANNEL(cioc), "nbd-server");
6200019455SDaniel P. Berrange     nbd_client_new(cioc, nbd_server->tlscreds, nbd_server->tlsauthz,
630c9390d9SEric Blake                    nbd_blockdev_client_closed);
646dd844dbSPaolo Bonzini }
656dd844dbSPaolo Bonzini 
661c8222b0SKevin Wolf static void nbd_update_server_watch(NBDServerData *s)
671c8222b0SKevin Wolf {
681c8222b0SKevin Wolf     if (!s->max_connections || s->connections < s->max_connections) {
691c8222b0SKevin Wolf         qio_net_listener_set_client_func(s->listener, nbd_accept, NULL, NULL);
701c8222b0SKevin Wolf     } else {
711c8222b0SKevin Wolf         qio_net_listener_set_client_func(s->listener, NULL, NULL, NULL);
721c8222b0SKevin Wolf     }
731c8222b0SKevin Wolf }
74ddffee39SDaniel P. Berrange 
75ddffee39SDaniel P. Berrange static void nbd_server_free(NBDServerData *server)
766dd844dbSPaolo Bonzini {
77ddffee39SDaniel P. Berrange     if (!server) {
78ddffee39SDaniel P. Berrange         return;
79ddffee39SDaniel P. Berrange     }
80ddffee39SDaniel P. Berrange 
81862172f4SDaniel P. Berrange     qio_net_listener_disconnect(server->listener);
82862172f4SDaniel P. Berrange     object_unref(OBJECT(server->listener));
83ddffee39SDaniel P. Berrange     if (server->tlscreds) {
84ddffee39SDaniel P. Berrange         object_unref(OBJECT(server->tlscreds));
85ddffee39SDaniel P. Berrange     }
8600019455SDaniel P. Berrange     g_free(server->tlsauthz);
87ddffee39SDaniel P. Berrange 
88ddffee39SDaniel P. Berrange     g_free(server);
89ddffee39SDaniel P. Berrange }
90ddffee39SDaniel P. Berrange 
91ddffee39SDaniel P. Berrange static QCryptoTLSCreds *nbd_get_tls_creds(const char *id, Error **errp)
92ddffee39SDaniel P. Berrange {
93ddffee39SDaniel P. Berrange     Object *obj;
94ddffee39SDaniel P. Berrange     QCryptoTLSCreds *creds;
95ddffee39SDaniel P. Berrange 
96ddffee39SDaniel P. Berrange     obj = object_resolve_path_component(
97ddffee39SDaniel P. Berrange         object_get_objects_root(), id);
98ddffee39SDaniel P. Berrange     if (!obj) {
99ddffee39SDaniel P. Berrange         error_setg(errp, "No TLS credentials with id '%s'",
100ddffee39SDaniel P. Berrange                    id);
101ddffee39SDaniel P. Berrange         return NULL;
102ddffee39SDaniel P. Berrange     }
103ddffee39SDaniel P. Berrange     creds = (QCryptoTLSCreds *)
104ddffee39SDaniel P. Berrange         object_dynamic_cast(obj, TYPE_QCRYPTO_TLS_CREDS);
105ddffee39SDaniel P. Berrange     if (!creds) {
106ddffee39SDaniel P. Berrange         error_setg(errp, "Object with id '%s' is not TLS credentials",
107ddffee39SDaniel P. Berrange                    id);
108ddffee39SDaniel P. Berrange         return NULL;
109ddffee39SDaniel P. Berrange     }
110ddffee39SDaniel P. Berrange 
111*7b3b6168SPhilippe Mathieu-Daudé     if (!qcrypto_tls_creds_check_endpoint(creds,
112*7b3b6168SPhilippe Mathieu-Daudé                                           QCRYPTO_TLS_CREDS_ENDPOINT_SERVER,
113*7b3b6168SPhilippe Mathieu-Daudé                                           errp)) {
114ddffee39SDaniel P. Berrange         return NULL;
115ddffee39SDaniel P. Berrange     }
116ddffee39SDaniel P. Berrange     object_ref(obj);
117ddffee39SDaniel P. Berrange     return creds;
118ddffee39SDaniel P. Berrange }
119ddffee39SDaniel P. Berrange 
120ddffee39SDaniel P. Berrange 
121bd269ebcSMarkus Armbruster void nbd_server_start(SocketAddress *addr, const char *tls_creds,
1221c8222b0SKevin Wolf                       const char *tls_authz, uint32_t max_connections,
1231c8222b0SKevin Wolf                       Error **errp)
124ddffee39SDaniel P. Berrange {
125ddffee39SDaniel P. Berrange     if (nbd_server) {
1266dd844dbSPaolo Bonzini         error_setg(errp, "NBD server already running");
1276dd844dbSPaolo Bonzini         return;
1286dd844dbSPaolo Bonzini     }
1296dd844dbSPaolo Bonzini 
130ddffee39SDaniel P. Berrange     nbd_server = g_new0(NBDServerData, 1);
1311c8222b0SKevin Wolf     nbd_server->max_connections = max_connections;
132862172f4SDaniel P. Berrange     nbd_server->listener = qio_net_listener_new();
133862172f4SDaniel P. Berrange 
134862172f4SDaniel P. Berrange     qio_net_listener_set_name(nbd_server->listener,
1350d73f725SDaniel P. Berrange                               "nbd-listener");
136862172f4SDaniel P. Berrange 
137582d4210SEric Blake     /*
138582d4210SEric Blake      * Because this server is persistent, a backlog of SOMAXCONN is
139582d4210SEric Blake      * better than trying to size it to max_connections.
140582d4210SEric Blake      */
141582d4210SEric Blake     if (qio_net_listener_open_sync(nbd_server->listener, addr, SOMAXCONN,
142582d4210SEric Blake                                    errp) < 0) {
143ddffee39SDaniel P. Berrange         goto error;
1446dd844dbSPaolo Bonzini     }
145ae398278SDaniel P. Berrange 
146bd269ebcSMarkus Armbruster     if (tls_creds) {
147ddffee39SDaniel P. Berrange         nbd_server->tlscreds = nbd_get_tls_creds(tls_creds, errp);
148ddffee39SDaniel P. Berrange         if (!nbd_server->tlscreds) {
149ddffee39SDaniel P. Berrange             goto error;
150ddffee39SDaniel P. Berrange         }
151ddffee39SDaniel P. Berrange 
152bd269ebcSMarkus Armbruster         /* TODO SOCKET_ADDRESS_TYPE_FD where fd has AF_INET or AF_INET6 */
153bd269ebcSMarkus Armbruster         if (addr->type != SOCKET_ADDRESS_TYPE_INET) {
154ddffee39SDaniel P. Berrange             error_setg(errp, "TLS is only supported with IPv4/IPv6");
155ddffee39SDaniel P. Berrange             goto error;
156ddffee39SDaniel P. Berrange         }
157ddffee39SDaniel P. Berrange     }
158ddffee39SDaniel P. Berrange 
15900019455SDaniel P. Berrange     nbd_server->tlsauthz = g_strdup(tls_authz);
16000019455SDaniel P. Berrange 
1611c8222b0SKevin Wolf     nbd_update_server_watch(nbd_server);
162ddffee39SDaniel P. Berrange 
163ddffee39SDaniel P. Berrange     return;
164ddffee39SDaniel P. Berrange 
165ddffee39SDaniel P. Berrange  error:
166ddffee39SDaniel P. Berrange     nbd_server_free(nbd_server);
167ddffee39SDaniel P. Berrange     nbd_server = NULL;
1686dd844dbSPaolo Bonzini }
1696dd844dbSPaolo Bonzini 
170eed8b691SKevin Wolf void nbd_server_start_options(NbdServerOptions *arg, Error **errp)
171eed8b691SKevin Wolf {
1721c8222b0SKevin Wolf     nbd_server_start(arg->addr, arg->tls_creds, arg->tls_authz,
1731c8222b0SKevin Wolf                      arg->max_connections, errp);
174eed8b691SKevin Wolf }
175eed8b691SKevin Wolf 
176bd269ebcSMarkus Armbruster void qmp_nbd_server_start(SocketAddressLegacy *addr,
177bd269ebcSMarkus Armbruster                           bool has_tls_creds, const char *tls_creds,
17800019455SDaniel P. Berrange                           bool has_tls_authz, const char *tls_authz,
1791c8222b0SKevin Wolf                           bool has_max_connections, uint32_t max_connections,
180bd269ebcSMarkus Armbruster                           Error **errp)
181bd269ebcSMarkus Armbruster {
182bd269ebcSMarkus Armbruster     SocketAddress *addr_flat = socket_address_flatten(addr);
183bd269ebcSMarkus Armbruster 
1841c8222b0SKevin Wolf     nbd_server_start(addr_flat, tls_creds, tls_authz, max_connections, errp);
185bd269ebcSMarkus Armbruster     qapi_free_SocketAddress(addr_flat);
186bd269ebcSMarkus Armbruster }
187bd269ebcSMarkus Armbruster 
188b6076afcSKevin Wolf void qmp_nbd_server_add(NbdServerAddOptions *arg, Error **errp)
18956ee8626SKevin Wolf {
1909b562c64SKevin Wolf     BlockExport *export;
1919b562c64SKevin Wolf     BlockDriverState *bs;
1929b562c64SKevin Wolf     BlockBackend *on_eject_blk;
193b6076afcSKevin Wolf     BlockExportOptions *export_opts;
1949b562c64SKevin Wolf 
1959b562c64SKevin Wolf     bs = bdrv_lookup_bs(arg->device, arg->device, errp);
1969b562c64SKevin Wolf     if (!bs) {
1979b562c64SKevin Wolf         return;
1989b562c64SKevin Wolf     }
1999b562c64SKevin Wolf 
200b6076afcSKevin Wolf     /*
201b6076afcSKevin Wolf      * block-export-add would default to the node-name, but we may have to use
202b6076afcSKevin Wolf      * the device name as a default here for compatibility.
203b6076afcSKevin Wolf      */
204b6076afcSKevin Wolf     if (!arg->has_name) {
2058675cbd6SEric Blake         arg->has_name = true;
2068675cbd6SEric Blake         arg->name = g_strdup(arg->device);
207b6076afcSKevin Wolf     }
208b6076afcSKevin Wolf 
209b6076afcSKevin Wolf     export_opts = g_new(BlockExportOptions, 1);
210b6076afcSKevin Wolf     *export_opts = (BlockExportOptions) {
21156ee8626SKevin Wolf         .type                   = BLOCK_EXPORT_TYPE_NBD,
212d53be9ceSKevin Wolf         .id                     = g_strdup(arg->name),
213b6076afcSKevin Wolf         .node_name              = g_strdup(bdrv_get_node_name(bs)),
21430dbc81dSKevin Wolf         .has_writable           = arg->has_writable,
21530dbc81dSKevin Wolf         .writable               = arg->writable,
21656ee8626SKevin Wolf     };
217cbad81ceSEric Blake     QAPI_CLONE_MEMBERS(BlockExportOptionsNbdBase, &export_opts->u.nbd,
2188675cbd6SEric Blake                        qapi_NbdServerAddOptions_base(arg));
219cbad81ceSEric Blake     if (arg->has_bitmap) {
220cbad81ceSEric Blake         export_opts->u.nbd.has_bitmaps = true;
221cbad81ceSEric Blake         QAPI_LIST_PREPEND(export_opts->u.nbd.bitmaps, g_strdup(arg->bitmap));
222cbad81ceSEric Blake     }
2239b562c64SKevin Wolf 
2249b562c64SKevin Wolf     /*
2259b562c64SKevin Wolf      * nbd-server-add doesn't complain when a read-only device should be
2269b562c64SKevin Wolf      * exported as writable, but simply downgrades it. This is an error with
2279b562c64SKevin Wolf      * block-export-add.
2289b562c64SKevin Wolf      */
2299b562c64SKevin Wolf     if (bdrv_is_read_only(bs)) {
23030dbc81dSKevin Wolf         export_opts->has_writable = true;
23130dbc81dSKevin Wolf         export_opts->writable = false;
2329b562c64SKevin Wolf     }
2339b562c64SKevin Wolf 
234b6076afcSKevin Wolf     export = blk_exp_add(export_opts, errp);
2359b562c64SKevin Wolf     if (!export) {
236b6076afcSKevin Wolf         goto fail;
2379b562c64SKevin Wolf     }
2389b562c64SKevin Wolf 
2399b562c64SKevin Wolf     /*
2409b562c64SKevin Wolf      * nbd-server-add removes the export when the named BlockBackend used for
2419b562c64SKevin Wolf      * @device goes away.
2429b562c64SKevin Wolf      */
2439b562c64SKevin Wolf     on_eject_blk = blk_by_name(arg->device);
2449b562c64SKevin Wolf     if (on_eject_blk) {
2459b562c64SKevin Wolf         nbd_export_set_on_eject_blk(export, on_eject_blk);
2469b562c64SKevin Wolf     }
247b6076afcSKevin Wolf 
248b6076afcSKevin Wolf fail:
249b6076afcSKevin Wolf     qapi_free_BlockExportOptions(export_opts);
2506dd844dbSPaolo Bonzini }
2516dd844dbSPaolo Bonzini 
252a3b0dc75SVladimir Sementsov-Ogievskiy void qmp_nbd_server_remove(const char *name,
2533c3bc462SKevin Wolf                            bool has_mode, BlockExportRemoveMode mode,
254a3b0dc75SVladimir Sementsov-Ogievskiy                            Error **errp)
255a3b0dc75SVladimir Sementsov-Ogievskiy {
2563c3bc462SKevin Wolf     BlockExport *exp;
257a3b0dc75SVladimir Sementsov-Ogievskiy 
2583c3bc462SKevin Wolf     exp = blk_exp_find(name);
2593c3bc462SKevin Wolf     if (exp && exp->drv->type != BLOCK_EXPORT_TYPE_NBD) {
2603c3bc462SKevin Wolf         error_setg(errp, "Block export '%s' is not an NBD export", name);
261a3b0dc75SVladimir Sementsov-Ogievskiy         return;
262a3b0dc75SVladimir Sementsov-Ogievskiy     }
263a3b0dc75SVladimir Sementsov-Ogievskiy 
2643c3bc462SKevin Wolf     qmp_block_export_del(name, has_mode, mode, errp);
265a3b0dc75SVladimir Sementsov-Ogievskiy }
266a3b0dc75SVladimir Sementsov-Ogievskiy 
2676dd844dbSPaolo Bonzini void qmp_nbd_server_stop(Error **errp)
2686dd844dbSPaolo Bonzini {
2697801c3a7SEric Blake     if (!nbd_server) {
2707801c3a7SEric Blake         error_setg(errp, "NBD server not running");
2717801c3a7SEric Blake         return;
2727801c3a7SEric Blake     }
2737801c3a7SEric Blake 
274bc4ee65bSKevin Wolf     blk_exp_close_all_type(BLOCK_EXPORT_TYPE_NBD);
2756dd844dbSPaolo Bonzini 
276ddffee39SDaniel P. Berrange     nbd_server_free(nbd_server);
277ddffee39SDaniel P. Berrange     nbd_server = NULL;
278fc6467eaSPaolo Bonzini }
279