1 /* 2 * Serving QEMU block devices via NBD 3 * 4 * Copyright (c) 2012 Red Hat, Inc. 5 * 6 * Author: Paolo Bonzini <pbonzini@redhat.com> 7 * 8 * This work is licensed under the terms of the GNU GPL, version 2 or 9 * later. See the COPYING file in the top-level directory. 10 */ 11 12 #include "qemu/osdep.h" 13 #include "sysemu/blockdev.h" 14 #include "sysemu/block-backend.h" 15 #include "hw/block/block.h" 16 #include "qapi/error.h" 17 #include "qapi/clone-visitor.h" 18 #include "qapi/qapi-visit-block-export.h" 19 #include "qapi/qapi-commands-block-export.h" 20 #include "block/nbd.h" 21 #include "io/channel-socket.h" 22 #include "io/net-listener.h" 23 24 typedef struct NBDServerData { 25 QIONetListener *listener; 26 QCryptoTLSCreds *tlscreds; 27 char *tlsauthz; 28 uint32_t max_connections; 29 uint32_t connections; 30 } NBDServerData; 31 32 static NBDServerData *nbd_server; 33 static int qemu_nbd_connections = -1; /* Non-negative if this is qemu-nbd */ 34 35 static void nbd_update_server_watch(NBDServerData *s); 36 37 void nbd_server_is_qemu_nbd(int max_connections) 38 { 39 qemu_nbd_connections = max_connections; 40 } 41 42 bool nbd_server_is_running(void) 43 { 44 return nbd_server || qemu_nbd_connections >= 0; 45 } 46 47 int nbd_server_max_connections(void) 48 { 49 return nbd_server ? nbd_server->max_connections : qemu_nbd_connections; 50 } 51 52 static void nbd_blockdev_client_closed(NBDClient *client, bool ignored) 53 { 54 nbd_client_put(client); 55 assert(nbd_server->connections > 0); 56 nbd_server->connections--; 57 nbd_update_server_watch(nbd_server); 58 } 59 60 static void nbd_accept(QIONetListener *listener, QIOChannelSocket *cioc, 61 gpointer opaque) 62 { 63 nbd_server->connections++; 64 nbd_update_server_watch(nbd_server); 65 66 qio_channel_set_name(QIO_CHANNEL(cioc), "nbd-server"); 67 /* TODO - expose handshake timeout as QMP option */ 68 nbd_client_new(cioc, NBD_DEFAULT_HANDSHAKE_MAX_SECS, 69 nbd_server->tlscreds, nbd_server->tlsauthz, 70 nbd_blockdev_client_closed, NULL); 71 } 72 73 static void nbd_update_server_watch(NBDServerData *s) 74 { 75 if (!s->max_connections || s->connections < s->max_connections) { 76 qio_net_listener_set_client_func(s->listener, nbd_accept, NULL, NULL); 77 } else { 78 qio_net_listener_set_client_func(s->listener, NULL, NULL, NULL); 79 } 80 } 81 82 static void nbd_server_free(NBDServerData *server) 83 { 84 if (!server) { 85 return; 86 } 87 88 qio_net_listener_disconnect(server->listener); 89 object_unref(OBJECT(server->listener)); 90 if (server->tlscreds) { 91 object_unref(OBJECT(server->tlscreds)); 92 } 93 g_free(server->tlsauthz); 94 95 g_free(server); 96 } 97 98 static QCryptoTLSCreds *nbd_get_tls_creds(const char *id, Error **errp) 99 { 100 Object *obj; 101 QCryptoTLSCreds *creds; 102 103 obj = object_resolve_path_component( 104 object_get_objects_root(), id); 105 if (!obj) { 106 error_setg(errp, "No TLS credentials with id '%s'", 107 id); 108 return NULL; 109 } 110 creds = (QCryptoTLSCreds *) 111 object_dynamic_cast(obj, TYPE_QCRYPTO_TLS_CREDS); 112 if (!creds) { 113 error_setg(errp, "Object with id '%s' is not TLS credentials", 114 id); 115 return NULL; 116 } 117 118 if (!qcrypto_tls_creds_check_endpoint(creds, 119 QCRYPTO_TLS_CREDS_ENDPOINT_SERVER, 120 errp)) { 121 return NULL; 122 } 123 object_ref(obj); 124 return creds; 125 } 126 127 128 void nbd_server_start(SocketAddress *addr, const char *tls_creds, 129 const char *tls_authz, uint32_t max_connections, 130 Error **errp) 131 { 132 if (nbd_server) { 133 error_setg(errp, "NBD server already running"); 134 return; 135 } 136 137 nbd_server = g_new0(NBDServerData, 1); 138 nbd_server->max_connections = max_connections; 139 nbd_server->listener = qio_net_listener_new(); 140 141 qio_net_listener_set_name(nbd_server->listener, 142 "nbd-listener"); 143 144 /* 145 * Because this server is persistent, a backlog of SOMAXCONN is 146 * better than trying to size it to max_connections. 147 */ 148 if (qio_net_listener_open_sync(nbd_server->listener, addr, SOMAXCONN, 149 errp) < 0) { 150 goto error; 151 } 152 153 if (tls_creds) { 154 nbd_server->tlscreds = nbd_get_tls_creds(tls_creds, errp); 155 if (!nbd_server->tlscreds) { 156 goto error; 157 } 158 } 159 160 nbd_server->tlsauthz = g_strdup(tls_authz); 161 162 nbd_update_server_watch(nbd_server); 163 164 return; 165 166 error: 167 nbd_server_free(nbd_server); 168 nbd_server = NULL; 169 } 170 171 void nbd_server_start_options(NbdServerOptions *arg, Error **errp) 172 { 173 nbd_server_start(arg->addr, arg->tls_creds, arg->tls_authz, 174 arg->max_connections, errp); 175 } 176 177 void qmp_nbd_server_start(SocketAddressLegacy *addr, 178 const char *tls_creds, 179 const char *tls_authz, 180 bool has_max_connections, uint32_t max_connections, 181 Error **errp) 182 { 183 SocketAddress *addr_flat = socket_address_flatten(addr); 184 185 nbd_server_start(addr_flat, tls_creds, tls_authz, max_connections, errp); 186 qapi_free_SocketAddress(addr_flat); 187 } 188 189 void qmp_nbd_server_add(NbdServerAddOptions *arg, Error **errp) 190 { 191 BlockExport *export; 192 BlockDriverState *bs; 193 BlockBackend *on_eject_blk; 194 BlockExportOptions *export_opts; 195 196 bs = bdrv_lookup_bs(arg->device, arg->device, errp); 197 if (!bs) { 198 return; 199 } 200 201 /* 202 * block-export-add would default to the node-name, but we may have to use 203 * the device name as a default here for compatibility. 204 */ 205 if (!arg->name) { 206 arg->name = g_strdup(arg->device); 207 } 208 209 export_opts = g_new(BlockExportOptions, 1); 210 *export_opts = (BlockExportOptions) { 211 .type = BLOCK_EXPORT_TYPE_NBD, 212 .id = g_strdup(arg->name), 213 .node_name = g_strdup(bdrv_get_node_name(bs)), 214 .has_writable = arg->has_writable, 215 .writable = arg->writable, 216 }; 217 QAPI_CLONE_MEMBERS(BlockExportOptionsNbdBase, &export_opts->u.nbd, 218 qapi_NbdServerAddOptions_base(arg)); 219 if (arg->bitmap) { 220 BlockDirtyBitmapOrStr *el = g_new(BlockDirtyBitmapOrStr, 1); 221 222 *el = (BlockDirtyBitmapOrStr) { 223 .type = QTYPE_QSTRING, 224 .u.local = g_strdup(arg->bitmap), 225 }; 226 export_opts->u.nbd.has_bitmaps = true; 227 QAPI_LIST_PREPEND(export_opts->u.nbd.bitmaps, el); 228 } 229 230 /* 231 * nbd-server-add doesn't complain when a read-only device should be 232 * exported as writable, but simply downgrades it. This is an error with 233 * block-export-add. 234 */ 235 if (bdrv_is_read_only(bs)) { 236 export_opts->has_writable = true; 237 export_opts->writable = false; 238 } 239 240 export = blk_exp_add(export_opts, errp); 241 if (!export) { 242 goto fail; 243 } 244 245 /* 246 * nbd-server-add removes the export when the named BlockBackend used for 247 * @device goes away. 248 */ 249 on_eject_blk = blk_by_name(arg->device); 250 if (on_eject_blk) { 251 nbd_export_set_on_eject_blk(export, on_eject_blk); 252 } 253 254 fail: 255 qapi_free_BlockExportOptions(export_opts); 256 } 257 258 void qmp_nbd_server_remove(const char *name, 259 bool has_mode, BlockExportRemoveMode mode, 260 Error **errp) 261 { 262 BlockExport *exp; 263 264 exp = blk_exp_find(name); 265 if (exp && exp->drv->type != BLOCK_EXPORT_TYPE_NBD) { 266 error_setg(errp, "Block export '%s' is not an NBD export", name); 267 return; 268 } 269 270 qmp_block_export_del(name, has_mode, mode, errp); 271 } 272 273 void qmp_nbd_server_stop(Error **errp) 274 { 275 if (!nbd_server) { 276 error_setg(errp, "NBD server not running"); 277 return; 278 } 279 280 blk_exp_close_all_type(BLOCK_EXPORT_TYPE_NBD); 281 282 nbd_server_free(nbd_server); 283 nbd_server = NULL; 284 } 285