nbd patches for 2019-01-21
Add 'qemu-nbd --list' for probing a remote NBD server's advertisements. - Eric Blake: 0/21 nbd: add qemu-nbd --list -----BEGIN PGP SIGNATURE----- iQEcBAABCAAGBQJcRktLAAoJEKeha0olJ0NqY8cIAJqcHDAuk1GwcDE/NIAASY9E 2PsaEL2pM65bEWUwsnVkFyRb4y4S4QX9VtAR/c9G6jR0YHuDvCCz7VGjiHh3eGso 9AWKzuh5C4mayAN5dTOlbjryOx7hlcz8wDDjP5OQNqIlBvmWVAnLeh5Kkqlc7KDk f1SH8kJfqegDAhKDeDhi/HGlL1UVzMr5A2sR6I1wbb5IbBru5JtAKzgqAmdBH893 lWZ9EDXqajwPCo8ASwZNyawmtmjx+VBeFjO2juA3qTWvf262roiB3XT5W/3n/CCb Er5CBwQQvSFBUfF9bRYDziyjwQwTXoIQJ6nuJLzPGx2cUpQbJ/svphoRQ6rBOjY= =gywW -----END PGP SIGNATURE----- Merge remote-tracking branch 'remotes/ericb/tags/pull-nbd-2019-01-21' into staging nbd patches for 2019-01-21 Add 'qemu-nbd --list' for probing a remote NBD server's advertisements. - Eric Blake: 0/21 nbd: add qemu-nbd --list # gpg: Signature made Mon 21 Jan 2019 22:44:27 GMT # gpg: using RSA key A7A16B4A2527436A # gpg: Good signature from "Eric Blake <eblake@redhat.com>" # gpg: aka "Eric Blake (Free Software Programmer) <ebb9@byu.net>" # gpg: aka "[jpeg image of size 6874]" # Primary key fingerprint: 71C2 CC22 B1C4 6029 27D2 F3AA A7A1 6B4A 2527 436A * remotes/ericb/tags/pull-nbd-2019-01-21: (21 commits) iotests: Enhance 223, 233 to cover 'qemu-nbd --list' nbd/client: Work around 3.0 bug for listing meta contexts qemu-nbd: Add --list option nbd/client: Add meta contexts to nbd_receive_export_list() nbd/client: Add nbd_receive_export_list() nbd/client: Refactor nbd_opt_go() to support NBD_OPT_INFO nbd/client: Pull out oldstyle size determination nbd/client: Split handshake into two functions nbd/client: Refactor return of nbd_receive_negotiate() nbd/client: Split out nbd_receive_one_meta_context() nbd/client: Split out nbd_send_meta_query() nbd/client: Change signature of nbd_negotiate_simple_meta_context() nbd/client: Move export name into NBDExportInfo nbd/client: Refactor nbd_receive_list() qemu-nbd: Avoid strtol open-coding nbd/server: Favor [u]int64_t over off_t nbd/server: Hoist length check to qmp_nbd_server_add qemu-nbd: Sanity check partition bounds qemu-nbd: Enhance man page maint: Allow for EXAMPLES in texi2pod ... Signed-off-by: Peter Maydell <peter.maydell@linaro.org>
This commit is contained in:
commit
952bc8b3c2
2
Makefile
2
Makefile
@ -860,6 +860,8 @@ docs/interop/qemu-qmp-ref.dvi docs/interop/qemu-qmp-ref.html \
|
||||
docs/interop/qemu-qmp-ref.txt docs/interop/qemu-qmp-ref.7: \
|
||||
docs/interop/qemu-qmp-ref.texi docs/interop/qemu-qmp-qapi.texi
|
||||
|
||||
$(filter %.1 %.7 %.8,$(DOCS)): scripts/texi2pod.pl
|
||||
|
||||
# Reports/Analysis
|
||||
|
||||
%/coverage-report.html:
|
||||
|
@ -249,11 +249,11 @@ static int nbd_parse_blockstatus_payload(NBDClientSession *client,
|
||||
}
|
||||
|
||||
context_id = payload_advance32(&payload);
|
||||
if (client->info.meta_base_allocation_id != context_id) {
|
||||
if (client->info.context_id != context_id) {
|
||||
error_setg(errp, "Protocol error: unexpected context id %d for "
|
||||
"NBD_REPLY_TYPE_BLOCK_STATUS, when negotiated context "
|
||||
"id is %d", context_id,
|
||||
client->info.meta_base_allocation_id);
|
||||
client->info.context_id);
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
@ -999,10 +999,11 @@ int nbd_client_init(BlockDriverState *bs,
|
||||
client->info.structured_reply = true;
|
||||
client->info.base_allocation = true;
|
||||
client->info.x_dirty_bitmap = g_strdup(x_dirty_bitmap);
|
||||
ret = nbd_receive_negotiate(QIO_CHANNEL(sioc), export,
|
||||
tlscreds, hostname,
|
||||
client->info.name = g_strdup(export ?: "");
|
||||
ret = nbd_receive_negotiate(QIO_CHANNEL(sioc), tlscreds, hostname,
|
||||
&client->ioc, &client->info, errp);
|
||||
g_free(client->info.x_dirty_bitmap);
|
||||
g_free(client->info.name);
|
||||
if (ret < 0) {
|
||||
logout("Failed to negotiate with the NBD server\n");
|
||||
return ret;
|
||||
|
@ -146,6 +146,7 @@ void qmp_nbd_server_add(const char *device, bool has_name, const char *name,
|
||||
BlockDriverState *bs = NULL;
|
||||
BlockBackend *on_eject_blk;
|
||||
NBDExport *exp;
|
||||
int64_t len;
|
||||
|
||||
if (!nbd_server) {
|
||||
error_setg(errp, "NBD server not running");
|
||||
@ -168,6 +169,13 @@ void qmp_nbd_server_add(const char *device, bool has_name, const char *name,
|
||||
return;
|
||||
}
|
||||
|
||||
len = bdrv_getlength(bs);
|
||||
if (len < 0) {
|
||||
error_setg_errno(errp, -len,
|
||||
"Failed to determine the NBD export's length");
|
||||
return;
|
||||
}
|
||||
|
||||
if (!has_writable) {
|
||||
writable = false;
|
||||
}
|
||||
@ -175,7 +183,7 @@ void qmp_nbd_server_add(const char *device, bool has_name, const char *name,
|
||||
writable = false;
|
||||
}
|
||||
|
||||
exp = nbd_export_new(bs, 0, -1, name, NULL, bitmap,
|
||||
exp = nbd_export_new(bs, 0, len, name, NULL, bitmap,
|
||||
writable ? 0 : NBD_FLAG_READ_ONLY,
|
||||
NULL, false, on_eject_blk, errp);
|
||||
if (!exp) {
|
||||
|
@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright (C) 2016-2017 Red Hat, Inc.
|
||||
* Copyright (C) 2016-2019 Red Hat, Inc.
|
||||
* Copyright (C) 2005 Anthony Liguori <anthony@codemonkey.ws>
|
||||
*
|
||||
* Network Block Device
|
||||
@ -263,26 +263,39 @@ struct NBDExportInfo {
|
||||
bool request_sizes;
|
||||
char *x_dirty_bitmap;
|
||||
|
||||
/* Set by client before nbd_receive_negotiate(), or by server results
|
||||
* during nbd_receive_export_list() */
|
||||
char *name; /* must be non-NULL */
|
||||
|
||||
/* In-out fields, set by client before nbd_receive_negotiate() and
|
||||
* updated by server results during nbd_receive_negotiate() */
|
||||
bool structured_reply;
|
||||
bool base_allocation; /* base:allocation context for NBD_CMD_BLOCK_STATUS */
|
||||
|
||||
/* Set by server results during nbd_receive_negotiate() */
|
||||
/* Set by server results during nbd_receive_negotiate() and
|
||||
* nbd_receive_export_list() */
|
||||
uint64_t size;
|
||||
uint16_t flags;
|
||||
uint32_t min_block;
|
||||
uint32_t opt_block;
|
||||
uint32_t max_block;
|
||||
|
||||
uint32_t meta_base_allocation_id;
|
||||
uint32_t context_id;
|
||||
|
||||
/* Set by server results during nbd_receive_export_list() */
|
||||
char *description;
|
||||
int n_contexts;
|
||||
char **contexts;
|
||||
};
|
||||
typedef struct NBDExportInfo NBDExportInfo;
|
||||
|
||||
int nbd_receive_negotiate(QIOChannel *ioc, const char *name,
|
||||
QCryptoTLSCreds *tlscreds, const char *hostname,
|
||||
QIOChannel **outioc, NBDExportInfo *info,
|
||||
Error **errp);
|
||||
int nbd_receive_negotiate(QIOChannel *ioc, QCryptoTLSCreds *tlscreds,
|
||||
const char *hostname, QIOChannel **outioc,
|
||||
NBDExportInfo *info, Error **errp);
|
||||
void nbd_free_export_list(NBDExportInfo *info, int count);
|
||||
int nbd_receive_export_list(QIOChannel *ioc, QCryptoTLSCreds *tlscreds,
|
||||
const char *hostname, NBDExportInfo **info,
|
||||
Error **errp);
|
||||
int nbd_init(int fd, QIOChannelSocket *sioc, NBDExportInfo *info,
|
||||
Error **errp);
|
||||
int nbd_send_request(QIOChannel *ioc, NBDRequest *request);
|
||||
@ -294,8 +307,8 @@ int nbd_errno_to_system_errno(int err);
|
||||
typedef struct NBDExport NBDExport;
|
||||
typedef struct NBDClient NBDClient;
|
||||
|
||||
NBDExport *nbd_export_new(BlockDriverState *bs, off_t dev_offset, off_t size,
|
||||
const char *name, const char *description,
|
||||
NBDExport *nbd_export_new(BlockDriverState *bs, uint64_t dev_offset,
|
||||
uint64_t size, const char *name, const char *desc,
|
||||
const char *bitmap, uint16_t nbdflags,
|
||||
void (*close)(NBDExport *), bool writethrough,
|
||||
BlockBackend *on_eject_blk, Error **errp);
|
||||
|
787
nbd/client.c
787
nbd/client.c
File diff suppressed because it is too large
Load Diff
24
nbd/server.c
24
nbd/server.c
@ -77,8 +77,8 @@ struct NBDExport {
|
||||
BlockBackend *blk;
|
||||
char *name;
|
||||
char *description;
|
||||
off_t dev_offset;
|
||||
off_t size;
|
||||
uint64_t dev_offset;
|
||||
uint64_t size;
|
||||
uint16_t nbdflags;
|
||||
QTAILQ_HEAD(, NBDClient) clients;
|
||||
QTAILQ_ENTRY(NBDExport) next;
|
||||
@ -1455,8 +1455,8 @@ static void nbd_eject_notifier(Notifier *n, void *data)
|
||||
nbd_export_close(exp);
|
||||
}
|
||||
|
||||
NBDExport *nbd_export_new(BlockDriverState *bs, off_t dev_offset, off_t size,
|
||||
const char *name, const char *description,
|
||||
NBDExport *nbd_export_new(BlockDriverState *bs, uint64_t dev_offset,
|
||||
uint64_t size, const char *name, const char *desc,
|
||||
const char *bitmap, uint16_t nbdflags,
|
||||
void (*close)(NBDExport *), bool writethrough,
|
||||
BlockBackend *on_eject_blk, Error **errp)
|
||||
@ -1495,17 +1495,13 @@ NBDExport *nbd_export_new(BlockDriverState *bs, off_t dev_offset, off_t size,
|
||||
exp->refcount = 1;
|
||||
QTAILQ_INIT(&exp->clients);
|
||||
exp->blk = blk;
|
||||
assert(dev_offset <= INT64_MAX);
|
||||
exp->dev_offset = dev_offset;
|
||||
exp->name = g_strdup(name);
|
||||
exp->description = g_strdup(description);
|
||||
exp->description = g_strdup(desc);
|
||||
exp->nbdflags = nbdflags;
|
||||
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;
|
||||
assert(size <= INT64_MAX - dev_offset);
|
||||
exp->size = QEMU_ALIGN_DOWN(size, BDRV_SECTOR_SIZE);
|
||||
|
||||
if (bitmap) {
|
||||
BdrvDirtyBitmap *bm = NULL;
|
||||
@ -2134,10 +2130,10 @@ static int nbd_co_receive_request(NBDRequestData *req, NBDRequest *request,
|
||||
return -EROFS;
|
||||
}
|
||||
if (request->from > client->exp->size ||
|
||||
request->from + request->len > client->exp->size) {
|
||||
request->len > client->exp->size - request->from) {
|
||||
error_setg(errp, "operation past EOF; From: %" PRIu64 ", Len: %" PRIu32
|
||||
", Size: %" PRIu64, request->from, request->len,
|
||||
(uint64_t)client->exp->size);
|
||||
client->exp->size);
|
||||
return (request->type == NBD_CMD_WRITE ||
|
||||
request->type == NBD_CMD_WRITE_ZEROES) ? -ENOSPC : -EINVAL;
|
||||
}
|
||||
|
@ -3,20 +3,21 @@ nbd_send_option_request(uint32_t opt, const char *name, uint32_t len) "Sending o
|
||||
nbd_receive_option_reply(uint32_t option, const char *optname, uint32_t type, const char *typename, uint32_t length) "Received option reply %" PRIu32" (%s), type %" PRIu32" (%s), len %" PRIu32
|
||||
nbd_server_error_msg(uint32_t err, const char *type, const char *msg) "server reported error 0x%" PRIx32 " (%s) with additional message: %s"
|
||||
nbd_reply_err_unsup(uint32_t option, const char *name) "server doesn't understand request %" PRIu32 " (%s), attempting fallback"
|
||||
nbd_opt_go_start(const char *name) "Attempting NBD_OPT_GO for export '%s'"
|
||||
nbd_opt_go_success(void) "Export is good to go"
|
||||
nbd_opt_go_info_unknown(int info, const char *name) "Ignoring unknown info %d (%s)"
|
||||
nbd_opt_go_info_block_size(uint32_t minimum, uint32_t preferred, uint32_t maximum) "Block sizes are 0x%" PRIx32 ", 0x%" PRIx32 ", 0x%" PRIx32
|
||||
nbd_receive_list(const char *name, const char *desc) "export list includes '%s', description '%s'"
|
||||
nbd_opt_info_go_start(const char *opt, const char *name) "Attempting %s for export '%s'"
|
||||
nbd_opt_info_go_success(const char *opt) "Export is ready after %s request"
|
||||
nbd_opt_info_unknown(int info, const char *name) "Ignoring unknown info %d (%s)"
|
||||
nbd_opt_info_block_size(uint32_t minimum, uint32_t preferred, uint32_t maximum) "Block sizes are 0x%" PRIx32 ", 0x%" PRIx32 ", 0x%" PRIx32
|
||||
nbd_receive_query_exports_start(const char *wantname) "Querying export list for '%s'"
|
||||
nbd_receive_query_exports_success(const char *wantname) "Found desired export name '%s'"
|
||||
nbd_receive_starttls_new_client(void) "Setting up TLS"
|
||||
nbd_receive_starttls_tls_handshake(void) "Starting TLS handshake"
|
||||
nbd_opt_meta_request(const char *context, const char *export) "Requesting to set meta context %s for export %s"
|
||||
nbd_opt_meta_reply(const char *context, uint32_t id) "Received mapping of context %s to id %" PRIu32
|
||||
nbd_receive_negotiate(void *tlscreds, const char *hostname) "Receiving negotiation tlscreds=%p hostname=%s"
|
||||
nbd_opt_meta_request(const char *optname, const char *context, const char *export) "Requesting %s %s for export %s"
|
||||
nbd_opt_meta_reply(const char *optname, const char *context, uint32_t id) "Received %s mapping of %s to id %" PRIu32
|
||||
nbd_start_negotiate(void *tlscreds, const char *hostname) "Receiving negotiation tlscreds=%p hostname=%s"
|
||||
nbd_receive_negotiate_magic(uint64_t magic) "Magic is 0x%" PRIx64
|
||||
nbd_receive_negotiate_server_flags(uint32_t globalflags) "Global flags are 0x%" PRIx32
|
||||
nbd_receive_negotiate_default_name(void) "Using default NBD export name \"\""
|
||||
nbd_receive_negotiate_name(const char *name) "Requesting NBD export name '%s'"
|
||||
nbd_receive_negotiate_size_flags(uint64_t size, uint16_t flags) "Size is %" PRIu64 ", export flags 0x%" PRIx16
|
||||
nbd_init_set_socket(void) "Setting NBD socket"
|
||||
nbd_init_set_block_size(unsigned long block_size) "Setting block size to %lu"
|
||||
|
224
qemu-nbd.c
224
qemu-nbd.c
@ -76,7 +76,8 @@ static void usage(const char *name)
|
||||
{
|
||||
(printf) (
|
||||
"Usage: %s [OPTIONS] FILE\n"
|
||||
"QEMU Disk Network Block Device Server\n"
|
||||
" or: %s -L [OPTIONS]\n"
|
||||
"QEMU Disk Network Block Device Utility\n"
|
||||
"\n"
|
||||
" -h, --help display this help and exit\n"
|
||||
" -V, --version output version information and exit\n"
|
||||
@ -98,6 +99,7 @@ static void usage(const char *name)
|
||||
" -B, --bitmap=NAME expose a persistent dirty bitmap\n"
|
||||
"\n"
|
||||
"General purpose options:\n"
|
||||
" -L, --list list exports available from another NBD server\n"
|
||||
" --object type,id=ID,... define an object such as 'secret' for providing\n"
|
||||
" passwords and/or encryption keys\n"
|
||||
" --tls-creds=ID use id of an earlier --object to provide TLS\n"
|
||||
@ -131,7 +133,7 @@ static void usage(const char *name)
|
||||
" --image-opts treat FILE as a full set of image options\n"
|
||||
"\n"
|
||||
QEMU_HELP_BOTTOM "\n"
|
||||
, name, NBD_DEFAULT_PORT, "DEVICE");
|
||||
, name, name, NBD_DEFAULT_PORT, "DEVICE");
|
||||
}
|
||||
|
||||
static void version(const char *name)
|
||||
@ -176,7 +178,7 @@ static void read_partition(uint8_t *p, struct partition_record *r)
|
||||
}
|
||||
|
||||
static int find_partition(BlockBackend *blk, int partition,
|
||||
off_t *offset, off_t *size)
|
||||
uint64_t *offset, uint64_t *size)
|
||||
{
|
||||
struct partition_record mbr[4];
|
||||
uint8_t data[MBR_SIZE];
|
||||
@ -243,6 +245,91 @@ static void termsig_handler(int signum)
|
||||
}
|
||||
|
||||
|
||||
static int qemu_nbd_client_list(SocketAddress *saddr, QCryptoTLSCreds *tls,
|
||||
const char *hostname)
|
||||
{
|
||||
int ret = EXIT_FAILURE;
|
||||
int rc;
|
||||
Error *err = NULL;
|
||||
QIOChannelSocket *sioc;
|
||||
NBDExportInfo *list;
|
||||
int i, j;
|
||||
|
||||
sioc = qio_channel_socket_new();
|
||||
if (qio_channel_socket_connect_sync(sioc, saddr, &err) < 0) {
|
||||
error_report_err(err);
|
||||
return EXIT_FAILURE;
|
||||
}
|
||||
rc = nbd_receive_export_list(QIO_CHANNEL(sioc), tls, hostname, &list,
|
||||
&err);
|
||||
if (rc < 0) {
|
||||
if (err) {
|
||||
error_report_err(err);
|
||||
}
|
||||
goto out;
|
||||
}
|
||||
printf("exports available: %d\n", rc);
|
||||
for (i = 0; i < rc; i++) {
|
||||
printf(" export: '%s'\n", list[i].name);
|
||||
if (list[i].description && *list[i].description) {
|
||||
printf(" description: %s\n", list[i].description);
|
||||
}
|
||||
if (list[i].flags & NBD_FLAG_HAS_FLAGS) {
|
||||
printf(" size: %" PRIu64 "\n", list[i].size);
|
||||
printf(" flags: 0x%x (", list[i].flags);
|
||||
if (list[i].flags & NBD_FLAG_READ_ONLY) {
|
||||
printf(" readonly");
|
||||
}
|
||||
if (list[i].flags & NBD_FLAG_SEND_FLUSH) {
|
||||
printf(" flush");
|
||||
}
|
||||
if (list[i].flags & NBD_FLAG_SEND_FUA) {
|
||||
printf(" fua");
|
||||
}
|
||||
if (list[i].flags & NBD_FLAG_ROTATIONAL) {
|
||||
printf(" rotational");
|
||||
}
|
||||
if (list[i].flags & NBD_FLAG_SEND_TRIM) {
|
||||
printf(" trim");
|
||||
}
|
||||
if (list[i].flags & NBD_FLAG_SEND_WRITE_ZEROES) {
|
||||
printf(" zeroes");
|
||||
}
|
||||
if (list[i].flags & NBD_FLAG_SEND_DF) {
|
||||
printf(" df");
|
||||
}
|
||||
if (list[i].flags & NBD_FLAG_CAN_MULTI_CONN) {
|
||||
printf(" multi");
|
||||
}
|
||||
if (list[i].flags & NBD_FLAG_SEND_RESIZE) {
|
||||
printf(" resize");
|
||||
}
|
||||
if (list[i].flags & NBD_FLAG_SEND_CACHE) {
|
||||
printf(" cache");
|
||||
}
|
||||
printf(" )\n");
|
||||
}
|
||||
if (list[i].min_block) {
|
||||
printf(" min block: %u\n", list[i].min_block);
|
||||
printf(" opt block: %u\n", list[i].opt_block);
|
||||
printf(" max block: %u\n", list[i].max_block);
|
||||
}
|
||||
if (list[i].n_contexts) {
|
||||
printf(" available meta contexts: %d\n", list[i].n_contexts);
|
||||
for (j = 0; j < list[i].n_contexts; j++) {
|
||||
printf(" %s\n", list[i].contexts[j]);
|
||||
}
|
||||
}
|
||||
}
|
||||
nbd_free_export_list(list, rc);
|
||||
|
||||
ret = EXIT_SUCCESS;
|
||||
out:
|
||||
object_unref(OBJECT(sioc));
|
||||
return ret;
|
||||
}
|
||||
|
||||
|
||||
#if HAVE_NBD_DEVICE
|
||||
static void *show_parts(void *arg)
|
||||
{
|
||||
@ -264,7 +351,7 @@ static void *show_parts(void *arg)
|
||||
static void *nbd_client_thread(void *arg)
|
||||
{
|
||||
char *device = arg;
|
||||
NBDExportInfo info = { .request_sizes = false, };
|
||||
NBDExportInfo info = { .request_sizes = false, .name = g_strdup("") };
|
||||
QIOChannelSocket *sioc;
|
||||
int fd;
|
||||
int ret;
|
||||
@ -279,7 +366,7 @@ static void *nbd_client_thread(void *arg)
|
||||
goto out;
|
||||
}
|
||||
|
||||
ret = nbd_receive_negotiate(QIO_CHANNEL(sioc), NULL,
|
||||
ret = nbd_receive_negotiate(QIO_CHANNEL(sioc),
|
||||
NULL, NULL, NULL, &info, &local_error);
|
||||
if (ret < 0) {
|
||||
if (local_error) {
|
||||
@ -318,6 +405,7 @@ static void *nbd_client_thread(void *arg)
|
||||
}
|
||||
close(fd);
|
||||
object_unref(OBJECT(sioc));
|
||||
g_free(info.name);
|
||||
kill(getpid(), SIGTERM);
|
||||
return (void *) EXIT_SUCCESS;
|
||||
|
||||
@ -326,6 +414,7 @@ out_fd:
|
||||
out_socket:
|
||||
object_unref(OBJECT(sioc));
|
||||
out:
|
||||
g_free(info.name);
|
||||
kill(getpid(), SIGTERM);
|
||||
return (void *) EXIT_FAILURE;
|
||||
}
|
||||
@ -423,7 +512,8 @@ static QemuOptsList qemu_object_opts = {
|
||||
|
||||
|
||||
|
||||
static QCryptoTLSCreds *nbd_get_tls_creds(const char *id, Error **errp)
|
||||
static QCryptoTLSCreds *nbd_get_tls_creds(const char *id, bool list,
|
||||
Error **errp)
|
||||
{
|
||||
Object *obj;
|
||||
QCryptoTLSCreds *creds;
|
||||
@ -443,10 +533,18 @@ static QCryptoTLSCreds *nbd_get_tls_creds(const char *id, Error **errp)
|
||||
return NULL;
|
||||
}
|
||||
|
||||
if (creds->endpoint != QCRYPTO_TLS_CREDS_ENDPOINT_SERVER) {
|
||||
error_setg(errp,
|
||||
"Expecting TLS credentials with a server endpoint");
|
||||
return NULL;
|
||||
if (list) {
|
||||
if (creds->endpoint != QCRYPTO_TLS_CREDS_ENDPOINT_CLIENT) {
|
||||
error_setg(errp,
|
||||
"Expecting TLS credentials with a client endpoint");
|
||||
return NULL;
|
||||
}
|
||||
} else {
|
||||
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;
|
||||
@ -469,7 +567,8 @@ static void setup_address_and_port(const char **address, const char **port)
|
||||
static const char *socket_activation_validate_opts(const char *device,
|
||||
const char *sockpath,
|
||||
const char *address,
|
||||
const char *port)
|
||||
const char *port,
|
||||
bool list)
|
||||
{
|
||||
if (device != NULL) {
|
||||
return "NBD device can't be set when using socket activation";
|
||||
@ -487,6 +586,10 @@ static const char *socket_activation_validate_opts(const char *device,
|
||||
return "TCP port number can't be set when using socket activation";
|
||||
}
|
||||
|
||||
if (list) {
|
||||
return "List mode is incompatible with socket activation";
|
||||
}
|
||||
|
||||
return NULL;
|
||||
}
|
||||
|
||||
@ -500,17 +603,17 @@ int main(int argc, char **argv)
|
||||
{
|
||||
BlockBackend *blk;
|
||||
BlockDriverState *bs;
|
||||
off_t dev_offset = 0;
|
||||
uint64_t dev_offset = 0;
|
||||
uint16_t nbdflags = 0;
|
||||
bool disconnect = false;
|
||||
const char *bindto = NULL;
|
||||
const char *port = NULL;
|
||||
char *sockpath = NULL;
|
||||
char *device = NULL;
|
||||
off_t fd_size;
|
||||
int64_t fd_size;
|
||||
QemuOpts *sn_opts = NULL;
|
||||
const char *sn_id_or_name = NULL;
|
||||
const char *sopt = "hVb:o:p:rsnP:c:dvk:e:f:tl:x:T:D:B:";
|
||||
const char *sopt = "hVb:o:p:rsnP:c:dvk:e:f:tl:x:T:D:B:L";
|
||||
struct option lopt[] = {
|
||||
{ "help", no_argument, NULL, 'h' },
|
||||
{ "version", no_argument, NULL, 'V' },
|
||||
@ -523,6 +626,7 @@ int main(int argc, char **argv)
|
||||
{ "bitmap", required_argument, NULL, 'B' },
|
||||
{ "connect", required_argument, NULL, 'c' },
|
||||
{ "disconnect", no_argument, NULL, 'd' },
|
||||
{ "list", no_argument, NULL, 'L' },
|
||||
{ "snapshot", no_argument, NULL, 's' },
|
||||
{ "load-snapshot", required_argument, NULL, 'l' },
|
||||
{ "nocache", no_argument, NULL, 'n' },
|
||||
@ -546,9 +650,8 @@ int main(int argc, char **argv)
|
||||
};
|
||||
int ch;
|
||||
int opt_ind = 0;
|
||||
char *end;
|
||||
int flags = BDRV_O_RDWR;
|
||||
int partition = -1;
|
||||
int partition = 0;
|
||||
int ret = 0;
|
||||
bool seen_cache = false;
|
||||
bool seen_discard = false;
|
||||
@ -558,7 +661,7 @@ int main(int argc, char **argv)
|
||||
Error *local_err = NULL;
|
||||
BlockdevDetectZeroesOptions detect_zeroes = BLOCKDEV_DETECT_ZEROES_OPTIONS_OFF;
|
||||
QDict *options = NULL;
|
||||
const char *export_name = ""; /* Default export name */
|
||||
const char *export_name = NULL; /* defaults to "" later for server mode */
|
||||
const char *export_description = NULL;
|
||||
const char *bitmap = NULL;
|
||||
const char *tlscredsid = NULL;
|
||||
@ -566,6 +669,7 @@ int main(int argc, char **argv)
|
||||
bool writethrough = true;
|
||||
char *trace_file = NULL;
|
||||
bool fork_process = false;
|
||||
bool list = false;
|
||||
int old_stderr = -1;
|
||||
unsigned socket_activation;
|
||||
|
||||
@ -660,13 +764,8 @@ int main(int argc, char **argv)
|
||||
port = optarg;
|
||||
break;
|
||||
case 'o':
|
||||
dev_offset = strtoll (optarg, &end, 0);
|
||||
if (*end) {
|
||||
error_report("Invalid offset `%s'", optarg);
|
||||
exit(EXIT_FAILURE);
|
||||
}
|
||||
if (dev_offset < 0) {
|
||||
error_report("Offset must be positive `%s'", optarg);
|
||||
if (qemu_strtou64(optarg, NULL, 0, &dev_offset) < 0) {
|
||||
error_report("Invalid offset '%s'", optarg);
|
||||
exit(EXIT_FAILURE);
|
||||
}
|
||||
break;
|
||||
@ -688,13 +787,9 @@ int main(int argc, char **argv)
|
||||
flags &= ~BDRV_O_RDWR;
|
||||
break;
|
||||
case 'P':
|
||||
partition = strtol(optarg, &end, 0);
|
||||
if (*end) {
|
||||
error_report("Invalid partition `%s'", optarg);
|
||||
exit(EXIT_FAILURE);
|
||||
}
|
||||
if (partition < 1 || partition > 8) {
|
||||
error_report("Invalid partition %d", partition);
|
||||
if (qemu_strtoi(optarg, NULL, 0, &partition) < 0 ||
|
||||
partition < 1 || partition > 8) {
|
||||
error_report("Invalid partition '%s'", optarg);
|
||||
exit(EXIT_FAILURE);
|
||||
}
|
||||
break;
|
||||
@ -715,15 +810,11 @@ int main(int argc, char **argv)
|
||||
device = optarg;
|
||||
break;
|
||||
case 'e':
|
||||
shared = strtol(optarg, &end, 0);
|
||||
if (*end) {
|
||||
if (qemu_strtoi(optarg, NULL, 0, &shared) < 0 ||
|
||||
shared < 1) {
|
||||
error_report("Invalid shared device number '%s'", optarg);
|
||||
exit(EXIT_FAILURE);
|
||||
}
|
||||
if (shared < 1) {
|
||||
error_report("Shared device number must be greater than 0");
|
||||
exit(EXIT_FAILURE);
|
||||
}
|
||||
break;
|
||||
case 'f':
|
||||
fmt = optarg;
|
||||
@ -772,13 +863,33 @@ int main(int argc, char **argv)
|
||||
case QEMU_NBD_OPT_FORK:
|
||||
fork_process = true;
|
||||
break;
|
||||
case 'L':
|
||||
list = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if ((argc - optind) != 1) {
|
||||
if (list) {
|
||||
if (argc != optind) {
|
||||
error_report("List mode is incompatible with a file name");
|
||||
exit(EXIT_FAILURE);
|
||||
}
|
||||
if (export_name || export_description || dev_offset || partition ||
|
||||
device || disconnect || fmt || sn_id_or_name || bitmap ||
|
||||
seen_aio || seen_discard || seen_cache) {
|
||||
error_report("List mode is incompatible with per-device settings");
|
||||
exit(EXIT_FAILURE);
|
||||
}
|
||||
if (fork_process) {
|
||||
error_report("List mode is incompatible with forking");
|
||||
exit(EXIT_FAILURE);
|
||||
}
|
||||
} else if ((argc - optind) != 1) {
|
||||
error_report("Invalid number of arguments");
|
||||
error_printf("Try `%s --help' for more information.\n", argv[0]);
|
||||
exit(EXIT_FAILURE);
|
||||
} else if (!export_name) {
|
||||
export_name = "";
|
||||
}
|
||||
|
||||
qemu_opts_foreach(&qemu_object_opts,
|
||||
@ -797,7 +908,8 @@ int main(int argc, char **argv)
|
||||
} else {
|
||||
/* Using socket activation - check user didn't use -p etc. */
|
||||
const char *err_msg = socket_activation_validate_opts(device, sockpath,
|
||||
bindto, port);
|
||||
bindto, port,
|
||||
list);
|
||||
if (err_msg != NULL) {
|
||||
error_report("%s", err_msg);
|
||||
exit(EXIT_FAILURE);
|
||||
@ -820,7 +932,7 @@ int main(int argc, char **argv)
|
||||
error_report("TLS is not supported with a host device");
|
||||
exit(EXIT_FAILURE);
|
||||
}
|
||||
tlscreds = nbd_get_tls_creds(tlscredsid, &local_err);
|
||||
tlscreds = nbd_get_tls_creds(tlscredsid, list, &local_err);
|
||||
if (local_err) {
|
||||
error_report("Failed to get TLS creds %s",
|
||||
error_get_pretty(local_err));
|
||||
@ -828,6 +940,11 @@ int main(int argc, char **argv)
|
||||
}
|
||||
}
|
||||
|
||||
if (list) {
|
||||
saddr = nbd_build_socket_address(sockpath, bindto, port);
|
||||
return qemu_nbd_client_list(saddr, tlscreds, bindto);
|
||||
}
|
||||
|
||||
#if !HAVE_NBD_DEVICE
|
||||
if (disconnect || device) {
|
||||
error_report("Kernel /dev/nbdN support not available");
|
||||
@ -1005,20 +1122,37 @@ int main(int argc, char **argv)
|
||||
}
|
||||
|
||||
if (dev_offset >= fd_size) {
|
||||
error_report("Offset (%lld) has to be smaller than the image size "
|
||||
"(%lld)",
|
||||
(long long int)dev_offset, (long long int)fd_size);
|
||||
error_report("Offset (%" PRIu64 ") has to be smaller than the image "
|
||||
"size (%" PRId64 ")", dev_offset, fd_size);
|
||||
exit(EXIT_FAILURE);
|
||||
}
|
||||
fd_size -= dev_offset;
|
||||
|
||||
if (partition != -1) {
|
||||
ret = find_partition(blk, partition, &dev_offset, &fd_size);
|
||||
if (partition) {
|
||||
uint64_t limit;
|
||||
|
||||
if (dev_offset) {
|
||||
error_report("Cannot request partition and offset together");
|
||||
exit(EXIT_FAILURE);
|
||||
}
|
||||
ret = find_partition(blk, partition, &dev_offset, &limit);
|
||||
if (ret < 0) {
|
||||
error_report("Could not find partition %d: %s", partition,
|
||||
strerror(-ret));
|
||||
exit(EXIT_FAILURE);
|
||||
}
|
||||
/*
|
||||
* MBR partition limits are (32-bit << 9); this assert lets
|
||||
* the compiler know that we can't overflow 64 bits.
|
||||
*/
|
||||
assert(dev_offset + limit >= dev_offset);
|
||||
if (dev_offset + limit > fd_size) {
|
||||
error_report("Discovered partition %d at offset %" PRIu64
|
||||
" size %" PRIu64 ", but size exceeds file length %"
|
||||
PRId64, partition, dev_offset, limit, fd_size);
|
||||
exit(EXIT_FAILURE);
|
||||
}
|
||||
fd_size = limit;
|
||||
}
|
||||
|
||||
export = nbd_export_new(bs, dev_offset, fd_size, export_name,
|
||||
|
119
qemu-nbd.texi
119
qemu-nbd.texi
@ -2,6 +2,8 @@
|
||||
@c man begin SYNOPSIS
|
||||
@command{qemu-nbd} [OPTION]... @var{filename}
|
||||
|
||||
@command{qemu-nbd} @option{-L} [OPTION]...
|
||||
|
||||
@command{qemu-nbd} @option{-d} @var{dev}
|
||||
@c man end
|
||||
@end example
|
||||
@ -10,11 +12,19 @@
|
||||
|
||||
Export a QEMU disk image using the NBD protocol.
|
||||
|
||||
Other uses:
|
||||
@itemize
|
||||
@item
|
||||
Bind a /dev/nbdX block device to a QEMU server (on Linux).
|
||||
@item
|
||||
As a client to query exports of a remote NBD server.
|
||||
@end itemize
|
||||
|
||||
@c man end
|
||||
|
||||
@c man begin OPTIONS
|
||||
@var{filename} is a disk image filename, or a set of block
|
||||
driver options if @var{--image-opts} is specified.
|
||||
driver options if @option{--image-opts} is specified.
|
||||
|
||||
@var{dev} is an NBD device.
|
||||
|
||||
@ -25,26 +35,29 @@ See the @code{qemu(1)} manual page for full details of the properties
|
||||
supported. The common object types that it makes sense to define are the
|
||||
@code{secret} object, which is used to supply passwords and/or encryption
|
||||
keys, and the @code{tls-creds} object, which is used to supply TLS
|
||||
credentials for the qemu-nbd server.
|
||||
credentials for the qemu-nbd server or client.
|
||||
@item -p, --port=@var{port}
|
||||
The TCP port to listen on (default @samp{10809})
|
||||
The TCP port to listen on as a server, or connect to as a client
|
||||
(default @samp{10809}).
|
||||
@item -o, --offset=@var{offset}
|
||||
The offset into the image
|
||||
The offset into the image.
|
||||
@item -b, --bind=@var{iface}
|
||||
The interface to bind to (default @samp{0.0.0.0})
|
||||
The interface to bind to as a server, or connect to as a client
|
||||
(default @samp{0.0.0.0}).
|
||||
@item -k, --socket=@var{path}
|
||||
Use a unix socket with path @var{path}
|
||||
Use a unix socket with path @var{path}.
|
||||
@item --image-opts
|
||||
Treat @var{filename} as a set of image options, instead of a plain
|
||||
filename. If this flag is specified, the @var{-f} flag should
|
||||
not be used, instead the '@code{format=}' option should be set.
|
||||
@item -f, --format=@var{fmt}
|
||||
Force the use of the block driver for format @var{fmt} instead of
|
||||
auto-detecting
|
||||
auto-detecting.
|
||||
@item -r, --read-only
|
||||
Export the disk as read-only
|
||||
Export the disk as read-only.
|
||||
@item -P, --partition=@var{num}
|
||||
Only expose partition @var{num}
|
||||
Only expose MBR partition @var{num}. Understands physical partitions
|
||||
1-4 and logical partitions 5-8.
|
||||
@item -B, --bitmap=@var{name}
|
||||
If @var{filename} has a qcow2 persistent bitmap @var{name}, expose
|
||||
that bitmap via the ``qemu:dirty-bitmap:@var{name}'' context
|
||||
@ -52,7 +65,7 @@ accessible through NBD_OPT_SET_META_CONTEXT.
|
||||
@item -s, --snapshot
|
||||
Use @var{filename} as an external snapshot, create a temporary
|
||||
file with backing_file=@var{filename}, redirect the write to
|
||||
the temporary one
|
||||
the temporary one.
|
||||
@item -l, --load-snapshot=@var{snapshot_param}
|
||||
Load an internal snapshot inside @var{filename} and export it
|
||||
as an read-only device, @var{snapshot_param} format is
|
||||
@ -76,31 +89,38 @@ driver-specific optimized zero write commands. @var{detect-zeroes} is one of
|
||||
converts a zero write to an unmap operation and can only be used if
|
||||
@var{discard} is set to @samp{unmap}. The default is @samp{off}.
|
||||
@item -c, --connect=@var{dev}
|
||||
Connect @var{filename} to NBD device @var{dev}
|
||||
Connect @var{filename} to NBD device @var{dev} (Linux only).
|
||||
@item -d, --disconnect
|
||||
Disconnect the device @var{dev}
|
||||
Disconnect the device @var{dev} (Linux only).
|
||||
@item -e, --shared=@var{num}
|
||||
Allow up to @var{num} clients to share the device (default @samp{1})
|
||||
Allow up to @var{num} clients to share the device (default
|
||||
@samp{1}). Safe for readers, but for now, consistency is not
|
||||
guaranteed between multiple writers.
|
||||
@item -t, --persistent
|
||||
Don't exit on the last connection
|
||||
Don't exit on the last connection.
|
||||
@item -x, --export-name=@var{name}
|
||||
Set the NBD volume export name. This switches the server to use
|
||||
the new style NBD protocol negotiation
|
||||
Set the NBD volume export name (default of a zero-length string).
|
||||
@item -D, --description=@var{description}
|
||||
Set the NBD volume export description, as a human-readable
|
||||
string. Requires the use of @option{-x}
|
||||
string.
|
||||
@item -L, --list
|
||||
Connect as a client and list all details about the exports exposed by
|
||||
a remote NBD server. This enables list mode, and is incompatible
|
||||
with options that change behavior related to a specific export (such as
|
||||
@option{--export-name}, @option{--offset}, ...).
|
||||
@item --tls-creds=ID
|
||||
Enable mandatory TLS encryption for the server by setting the ID
|
||||
of the TLS credentials object previously created with the --object
|
||||
option.
|
||||
option; or provide the credentials needed for connecting as a client
|
||||
in list mode.
|
||||
@item --fork
|
||||
Fork off the server process and exit the parent once the server is running.
|
||||
@item -v, --verbose
|
||||
Display extra debugging information
|
||||
Display extra debugging information.
|
||||
@item -h, --help
|
||||
Display this help and exit
|
||||
Display this help and exit.
|
||||
@item -V, --version
|
||||
Display version information and exit
|
||||
Display version information and exit.
|
||||
@item -T, --trace [[enable=]@var{pattern}][,events=@var{file}][,file=@var{file}]
|
||||
@findex --trace
|
||||
@include qemu-option-trace.texi
|
||||
@ -108,6 +128,63 @@ Display version information and exit
|
||||
|
||||
@c man end
|
||||
|
||||
@c man begin EXAMPLES
|
||||
Start a server listening on port 10809 that exposes only the
|
||||
guest-visible contents of a qcow2 file, with no TLS encryption, and
|
||||
with the default export name (an empty string). The command is
|
||||
one-shot, and will block until the first successful client
|
||||
disconnects:
|
||||
|
||||
@example
|
||||
qemu-nbd -f qcow2 file.qcow2
|
||||
@end example
|
||||
|
||||
Start a long-running server listening with encryption on port 10810,
|
||||
and require clients to have a correct X.509 certificate to connect to
|
||||
a 1 megabyte subset of a raw file, using the export name 'subset':
|
||||
|
||||
@example
|
||||
qemu-nbd \
|
||||
--object tls-creds-x509,id=tls0,endpoint=server,dir=/path/to/qemutls \
|
||||
--tls-creds tls0 -t -x subset -p 10810 \
|
||||
--image-opts driver=raw,offset=1M,size=1M,file.driver=file,file.filename=file.raw
|
||||
@end example
|
||||
|
||||
Serve a read-only copy of just the first MBR partition of a guest
|
||||
image over a Unix socket with as many as 5 simultaneous readers, with
|
||||
a persistent process forked as a daemon:
|
||||
|
||||
@example
|
||||
qemu-nbd --fork --persistent --shared=5 --socket=/path/to/sock \
|
||||
--partition=1 --read-only --format=qcow2 file.qcow2
|
||||
@end example
|
||||
|
||||
Expose the guest-visible contents of a qcow2 file via a block device
|
||||
/dev/nbd0 (and possibly creating /dev/nbd0p1 and friends for
|
||||
partitions found within), then disconnect the device when done.
|
||||
Access to bind qemu-nbd to an /dev/nbd device generally requires root
|
||||
privileges, and may also require the execution of @code{modprobe nbd}
|
||||
to enable the kernel NBD client module. @emph{CAUTION}: Do not use
|
||||
this method to mount filesystems from an untrusted guest image - a
|
||||
malicious guest may have prepared the image to attempt to trigger
|
||||
kernel bugs in partition probing or file system mounting.
|
||||
|
||||
@example
|
||||
qemu-nbd -c /dev/nbd0 -f qcow2 file.qcow2
|
||||
qemu-nbd -d /dev/nbd0
|
||||
@end example
|
||||
|
||||
Query a remote server to see details about what export(s) it is
|
||||
serving on port 10809, and authenticating via PSK:
|
||||
|
||||
@example
|
||||
qemu-nbd \
|
||||
--object tls-creds-psk,id=tls0,dir=/tmp/keys,username=eblake,endpoint=client \
|
||||
--tls-creds tls0 -L -b remote.example.com
|
||||
@end example
|
||||
|
||||
@c man end
|
||||
|
||||
@ignore
|
||||
|
||||
@setfilename qemu-nbd
|
||||
|
@ -398,7 +398,7 @@ $sects{NAME} = "$fn \- $tl\n";
|
||||
$sects{FOOTNOTES} .= "=back\n" if exists $sects{FOOTNOTES};
|
||||
|
||||
for $sect (qw(NAME SYNOPSIS DESCRIPTION OPTIONS ENVIRONMENT FILES
|
||||
BUGS NOTES FOOTNOTES SEEALSO AUTHOR COPYRIGHT)) {
|
||||
BUGS NOTES FOOTNOTES EXAMPLES SEEALSO AUTHOR COPYRIGHT)) {
|
||||
if(exists $sects{$sect}) {
|
||||
$head = $sect;
|
||||
$head =~ s/SEEALSO/SEE ALSO/;
|
||||
|
@ -127,6 +127,7 @@ _send_qemu_cmd $QEMU_HANDLE '{"execute":"nbd-server-start",
|
||||
_send_qemu_cmd $QEMU_HANDLE '{"execute":"nbd-server-start",
|
||||
"arguments":{"addr":{"type":"unix",
|
||||
"data":{"path":"'"$TEST_DIR/nbd"1'"}}}}' "error" # Attempt second server
|
||||
$QEMU_NBD_PROG -L -k "$TEST_DIR/nbd"
|
||||
_send_qemu_cmd $QEMU_HANDLE '{"execute":"nbd-server-add",
|
||||
"arguments":{"device":"n", "bitmap":"b"}}' "return"
|
||||
_send_qemu_cmd $QEMU_HANDLE '{"execute":"nbd-server-add",
|
||||
@ -142,6 +143,7 @@ _send_qemu_cmd $QEMU_HANDLE '{"execute":"nbd-server-add",
|
||||
_send_qemu_cmd $QEMU_HANDLE '{"execute":"nbd-server-add",
|
||||
"arguments":{"device":"n", "name":"n2", "writable":true,
|
||||
"bitmap":"b2"}}' "return"
|
||||
$QEMU_NBD_PROG -L -k "$TEST_DIR/nbd"
|
||||
|
||||
echo
|
||||
echo "=== Contrast normal status to large granularity dirty-bitmap ==="
|
||||
|
@ -30,12 +30,32 @@ wrote 2097152/2097152 bytes at offset 2097152
|
||||
{"error": {"class": "GenericError", "desc": "NBD server not running"}}
|
||||
{"return": {}}
|
||||
{"error": {"class": "GenericError", "desc": "NBD server already running"}}
|
||||
exports available: 0
|
||||
{"return": {}}
|
||||
{"error": {"class": "GenericError", "desc": "Cannot find device=nosuch nor node_name=nosuch"}}
|
||||
{"error": {"class": "GenericError", "desc": "NBD server already has export named 'n'"}}
|
||||
{"error": {"class": "GenericError", "desc": "Enabled bitmap 'b2' incompatible with readonly export"}}
|
||||
{"error": {"class": "GenericError", "desc": "Bitmap 'b3' is not found"}}
|
||||
{"return": {}}
|
||||
exports available: 2
|
||||
export: 'n'
|
||||
size: 4194304
|
||||
flags: 0x4ef ( readonly flush fua trim zeroes df cache )
|
||||
min block: 512
|
||||
opt block: 4096
|
||||
max block: 33554432
|
||||
available meta contexts: 2
|
||||
base:allocation
|
||||
qemu:dirty-bitmap:b
|
||||
export: 'n2'
|
||||
size: 4194304
|
||||
flags: 0x4ed ( flush fua trim zeroes df cache )
|
||||
min block: 512
|
||||
opt block: 4096
|
||||
max block: 33554432
|
||||
available meta contexts: 2
|
||||
base:allocation
|
||||
qemu:dirty-bitmap:b2
|
||||
|
||||
=== Contrast normal status to large granularity dirty-bitmap ===
|
||||
|
||||
|
@ -2,7 +2,7 @@
|
||||
#
|
||||
# Test NBD TLS certificate / authorization integration
|
||||
#
|
||||
# Copyright (C) 2018 Red Hat, Inc.
|
||||
# Copyright (C) 2018-2019 Red Hat, Inc.
|
||||
#
|
||||
# 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
|
||||
@ -30,6 +30,7 @@ _cleanup()
|
||||
{
|
||||
nbd_server_stop
|
||||
_cleanup_test_img
|
||||
rm -f "$TEST_DIR/server.log"
|
||||
tls_x509_cleanup
|
||||
}
|
||||
trap "_cleanup; exit \$status" 0 1 2 3 15
|
||||
@ -66,12 +67,14 @@ $QEMU_IO -c 'w -P 0x11 1m 1m' "$TEST_IMG" | _filter_qemu_io
|
||||
|
||||
echo
|
||||
echo "== check TLS client to plain server fails =="
|
||||
nbd_server_start_tcp_socket -f $IMGFMT "$TEST_IMG"
|
||||
nbd_server_start_tcp_socket -f $IMGFMT "$TEST_IMG" 2> "$TEST_DIR/server.log"
|
||||
|
||||
$QEMU_IMG info --image-opts \
|
||||
--object tls-creds-x509,dir=${tls_dir}/client1,endpoint=client,id=tls0 \
|
||||
obj=tls-creds-x509,dir=${tls_dir}/client1,endpoint=client,id=tls0
|
||||
$QEMU_IMG info --image-opts --object $obj \
|
||||
driver=nbd,host=$nbd_tcp_addr,port=$nbd_tcp_port,tls-creds=tls0 \
|
||||
2>&1 | sed "s/$nbd_tcp_port/PORT/g"
|
||||
$QEMU_NBD_PROG -L -b $nbd_tcp_addr -p $nbd_tcp_port --object $obj \
|
||||
--tls-creds=tls0
|
||||
|
||||
nbd_server_stop
|
||||
|
||||
@ -81,23 +84,28 @@ echo "== check plain client to TLS server fails =="
|
||||
nbd_server_start_tcp_socket \
|
||||
--object tls-creds-x509,dir=${tls_dir}/server1,endpoint=server,id=tls0,verify-peer=yes \
|
||||
--tls-creds tls0 \
|
||||
-f $IMGFMT "$TEST_IMG"
|
||||
-f $IMGFMT "$TEST_IMG" 2>> "$TEST_DIR/server.log"
|
||||
|
||||
$QEMU_IMG info nbd://localhost:$nbd_tcp_port 2>&1 | sed "s/$nbd_tcp_port/PORT/g"
|
||||
$QEMU_NBD_PROG -L -b $nbd_tcp_addr -p $nbd_tcp_port
|
||||
|
||||
echo
|
||||
echo "== check TLS works =="
|
||||
$QEMU_IMG info --image-opts \
|
||||
--object tls-creds-x509,dir=${tls_dir}/client1,endpoint=client,id=tls0 \
|
||||
obj=tls-creds-x509,dir=${tls_dir}/client1,endpoint=client,id=tls0
|
||||
$QEMU_IMG info --image-opts --object $obj \
|
||||
driver=nbd,host=$nbd_tcp_addr,port=$nbd_tcp_port,tls-creds=tls0 \
|
||||
2>&1 | sed "s/$nbd_tcp_port/PORT/g"
|
||||
$QEMU_NBD_PROG -L -b $nbd_tcp_addr -p $nbd_tcp_port --object $obj \
|
||||
--tls-creds=tls0
|
||||
|
||||
echo
|
||||
echo "== check TLS with different CA fails =="
|
||||
$QEMU_IMG info --image-opts \
|
||||
--object tls-creds-x509,dir=${tls_dir}/client2,endpoint=client,id=tls0 \
|
||||
obj=tls-creds-x509,dir=${tls_dir}/client2,endpoint=client,id=tls0
|
||||
$QEMU_IMG info --image-opts --object $obj \
|
||||
driver=nbd,host=$nbd_tcp_addr,port=$nbd_tcp_port,tls-creds=tls0 \
|
||||
2>&1 | sed "s/$nbd_tcp_port/PORT/g"
|
||||
$QEMU_NBD_PROG -L -b $nbd_tcp_addr -p $nbd_tcp_port --object $obj \
|
||||
--tls-creds=tls0
|
||||
|
||||
echo
|
||||
echo "== perform I/O over TLS =="
|
||||
@ -109,6 +117,10 @@ $QEMU_IO -c 'r -P 0x11 1m 1m' -c 'w -P 0x22 1m 1m' --image-opts \
|
||||
|
||||
$QEMU_IO -f $IMGFMT -r -U -c 'r -P 0x22 1m 1m' "$TEST_IMG" | _filter_qemu_io
|
||||
|
||||
echo
|
||||
echo "== final server log =="
|
||||
cat "$TEST_DIR/server.log"
|
||||
|
||||
# success, all done
|
||||
echo "*** done"
|
||||
rm -f $seq.full
|
||||
|
@ -15,20 +15,33 @@ wrote 1048576/1048576 bytes at offset 1048576
|
||||
== check TLS client to plain server fails ==
|
||||
qemu-img: Could not open 'driver=nbd,host=127.0.0.1,port=PORT,tls-creds=tls0': Denied by server for option 5 (starttls)
|
||||
server reported: TLS not configured
|
||||
qemu-nbd: Denied by server for option 5 (starttls)
|
||||
server reported: TLS not configured
|
||||
|
||||
== check plain client to TLS server fails ==
|
||||
qemu-img: Could not open 'nbd://localhost:PORT': TLS negotiation required before option 8 (structured reply)
|
||||
server reported: Option 0x8 not permitted before TLS
|
||||
qemu-nbd: TLS negotiation required before option 8 (structured reply)
|
||||
server reported: Option 0x8 not permitted before TLS
|
||||
|
||||
== check TLS works ==
|
||||
image: nbd://127.0.0.1:PORT
|
||||
file format: nbd
|
||||
virtual size: 64M (67108864 bytes)
|
||||
disk size: unavailable
|
||||
exports available: 1
|
||||
export: ''
|
||||
size: 67108864
|
||||
flags: 0x4ed ( flush fua trim zeroes df cache )
|
||||
min block: 512
|
||||
opt block: 4096
|
||||
max block: 33554432
|
||||
available meta contexts: 1
|
||||
base:allocation
|
||||
|
||||
== check TLS with different CA fails ==
|
||||
qemu-nbd: option negotiation failed: Verify failed: No certificate was found.
|
||||
qemu-img: Could not open 'driver=nbd,host=127.0.0.1,port=PORT,tls-creds=tls0': The certificate hasn't got a known issuer
|
||||
qemu-nbd: The certificate hasn't got a known issuer
|
||||
|
||||
== perform I/O over TLS ==
|
||||
read 1048576/1048576 bytes at offset 1048576
|
||||
@ -37,4 +50,8 @@ wrote 1048576/1048576 bytes at offset 1048576
|
||||
1 MiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec)
|
||||
read 1048576/1048576 bytes at offset 1048576
|
||||
1 MiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec)
|
||||
|
||||
== final server log ==
|
||||
qemu-nbd: option negotiation failed: Verify failed: No certificate was found.
|
||||
qemu-nbd: option negotiation failed: Verify failed: No certificate was found.
|
||||
*** done
|
||||
|
Loading…
Reference in New Issue
Block a user