mirror of
https://gitlab.com/qemu-project/qemu
synced 2024-11-05 20:35:44 +00:00
nbd: Let server know when client gives up negotiation
The NBD spec says that a client should send NBD_OPT_ABORT rather than just dropping the connection, if the client doesn't like something the server sent during option negotiation. This is a best-effort attempt only, and can only be done in places where we know the server is still in sync with what we've sent, whether or not we've read everything the server has sent. Technically, the server then has to reply with NBD_REP_ACK, but it's not worth complicating the client to wait around for that reply. Signed-off-by: Eric Blake <eblake@redhat.com> Message-Id: <1476469998-28592-10-git-send-email-eblake@redhat.com> Signed-off-by: Paolo Bonzini <pbonzini@redhat.com>
This commit is contained in:
parent
c8a3a1b6c4
commit
2cdbf41362
1 changed files with 30 additions and 0 deletions
30
nbd/client.c
30
nbd/client.c
|
@ -111,6 +111,19 @@ static int nbd_send_option_request(QIOChannel *ioc, uint32_t opt,
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* Send NBD_OPT_ABORT as a courtesy to let the server know that we are
|
||||||
|
* not going to attempt further negotiation. */
|
||||||
|
static void nbd_send_opt_abort(QIOChannel *ioc)
|
||||||
|
{
|
||||||
|
/* Technically, a compliant server is supposed to reply to us; but
|
||||||
|
* older servers disconnected instead. At any rate, we're allowed
|
||||||
|
* to disconnect without waiting for the server reply, so we don't
|
||||||
|
* even care if the request makes it to the server, let alone
|
||||||
|
* waiting around for whether the server replies. */
|
||||||
|
nbd_send_option_request(ioc, NBD_OPT_ABORT, 0, NULL, NULL);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
/* Receive the header of an option reply, which should match the given
|
/* Receive the header of an option reply, which should match the given
|
||||||
* opt. Read through the length field, but NOT the length bytes of
|
* opt. Read through the length field, but NOT the length bytes of
|
||||||
* payload. Return 0 if successful, -1 with errp set if it is
|
* payload. Return 0 if successful, -1 with errp set if it is
|
||||||
|
@ -121,6 +134,7 @@ static int nbd_receive_option_reply(QIOChannel *ioc, uint32_t opt,
|
||||||
QEMU_BUILD_BUG_ON(sizeof(*reply) != 20);
|
QEMU_BUILD_BUG_ON(sizeof(*reply) != 20);
|
||||||
if (read_sync(ioc, reply, sizeof(*reply)) != sizeof(*reply)) {
|
if (read_sync(ioc, reply, sizeof(*reply)) != sizeof(*reply)) {
|
||||||
error_setg(errp, "failed to read option reply");
|
error_setg(errp, "failed to read option reply");
|
||||||
|
nbd_send_opt_abort(ioc);
|
||||||
return -1;
|
return -1;
|
||||||
}
|
}
|
||||||
be64_to_cpus(&reply->magic);
|
be64_to_cpus(&reply->magic);
|
||||||
|
@ -133,11 +147,13 @@ static int nbd_receive_option_reply(QIOChannel *ioc, uint32_t opt,
|
||||||
|
|
||||||
if (reply->magic != NBD_REP_MAGIC) {
|
if (reply->magic != NBD_REP_MAGIC) {
|
||||||
error_setg(errp, "Unexpected option reply magic");
|
error_setg(errp, "Unexpected option reply magic");
|
||||||
|
nbd_send_opt_abort(ioc);
|
||||||
return -1;
|
return -1;
|
||||||
}
|
}
|
||||||
if (reply->option != opt) {
|
if (reply->option != opt) {
|
||||||
error_setg(errp, "Unexpected option type %x expected %x",
|
error_setg(errp, "Unexpected option type %x expected %x",
|
||||||
reply->option, opt);
|
reply->option, opt);
|
||||||
|
nbd_send_opt_abort(ioc);
|
||||||
return -1;
|
return -1;
|
||||||
}
|
}
|
||||||
return 0;
|
return 0;
|
||||||
|
@ -206,6 +222,9 @@ static int nbd_handle_reply_err(QIOChannel *ioc, nbd_opt_reply *reply,
|
||||||
|
|
||||||
cleanup:
|
cleanup:
|
||||||
g_free(msg);
|
g_free(msg);
|
||||||
|
if (result < 0) {
|
||||||
|
nbd_send_opt_abort(ioc);
|
||||||
|
}
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -229,25 +248,30 @@ static int nbd_receive_list(QIOChannel *ioc, char **name, Error **errp)
|
||||||
if (reply.type == NBD_REP_ACK) {
|
if (reply.type == NBD_REP_ACK) {
|
||||||
if (len != 0) {
|
if (len != 0) {
|
||||||
error_setg(errp, "length too long for option end");
|
error_setg(errp, "length too long for option end");
|
||||||
|
nbd_send_opt_abort(ioc);
|
||||||
return -1;
|
return -1;
|
||||||
}
|
}
|
||||||
} else if (reply.type == NBD_REP_SERVER) {
|
} else if (reply.type == NBD_REP_SERVER) {
|
||||||
if (len < sizeof(namelen) || len > NBD_MAX_BUFFER_SIZE) {
|
if (len < sizeof(namelen) || len > NBD_MAX_BUFFER_SIZE) {
|
||||||
error_setg(errp, "incorrect option length %" PRIu32, len);
|
error_setg(errp, "incorrect option length %" PRIu32, len);
|
||||||
|
nbd_send_opt_abort(ioc);
|
||||||
return -1;
|
return -1;
|
||||||
}
|
}
|
||||||
if (read_sync(ioc, &namelen, sizeof(namelen)) != sizeof(namelen)) {
|
if (read_sync(ioc, &namelen, sizeof(namelen)) != sizeof(namelen)) {
|
||||||
error_setg(errp, "failed to read option name length");
|
error_setg(errp, "failed to read option name length");
|
||||||
|
nbd_send_opt_abort(ioc);
|
||||||
return -1;
|
return -1;
|
||||||
}
|
}
|
||||||
namelen = be32_to_cpu(namelen);
|
namelen = be32_to_cpu(namelen);
|
||||||
len -= sizeof(namelen);
|
len -= sizeof(namelen);
|
||||||
if (len < namelen) {
|
if (len < namelen) {
|
||||||
error_setg(errp, "incorrect option name length");
|
error_setg(errp, "incorrect option name length");
|
||||||
|
nbd_send_opt_abort(ioc);
|
||||||
return -1;
|
return -1;
|
||||||
}
|
}
|
||||||
if (namelen > NBD_MAX_NAME_SIZE) {
|
if (namelen > NBD_MAX_NAME_SIZE) {
|
||||||
error_setg(errp, "export name length too long %" PRIu32, namelen);
|
error_setg(errp, "export name length too long %" PRIu32, namelen);
|
||||||
|
nbd_send_opt_abort(ioc);
|
||||||
return -1;
|
return -1;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -256,6 +280,7 @@ static int nbd_receive_list(QIOChannel *ioc, char **name, Error **errp)
|
||||||
error_setg(errp, "failed to read export name");
|
error_setg(errp, "failed to read export name");
|
||||||
g_free(*name);
|
g_free(*name);
|
||||||
*name = NULL;
|
*name = NULL;
|
||||||
|
nbd_send_opt_abort(ioc);
|
||||||
return -1;
|
return -1;
|
||||||
}
|
}
|
||||||
(*name)[namelen] = '\0';
|
(*name)[namelen] = '\0';
|
||||||
|
@ -267,6 +292,7 @@ static int nbd_receive_list(QIOChannel *ioc, char **name, Error **errp)
|
||||||
g_free(*name);
|
g_free(*name);
|
||||||
g_free(buf);
|
g_free(buf);
|
||||||
*name = NULL;
|
*name = NULL;
|
||||||
|
nbd_send_opt_abort(ioc);
|
||||||
return -1;
|
return -1;
|
||||||
}
|
}
|
||||||
buf[len] = '\0';
|
buf[len] = '\0';
|
||||||
|
@ -276,6 +302,7 @@ static int nbd_receive_list(QIOChannel *ioc, char **name, Error **errp)
|
||||||
} else {
|
} else {
|
||||||
error_setg(errp, "Unexpected reply type %" PRIx32 " expected %x",
|
error_setg(errp, "Unexpected reply type %" PRIx32 " expected %x",
|
||||||
reply.type, NBD_REP_SERVER);
|
reply.type, NBD_REP_SERVER);
|
||||||
|
nbd_send_opt_abort(ioc);
|
||||||
return -1;
|
return -1;
|
||||||
}
|
}
|
||||||
return 1;
|
return 1;
|
||||||
|
@ -325,6 +352,7 @@ static int nbd_receive_query_exports(QIOChannel *ioc,
|
||||||
|
|
||||||
if (!foundExport) {
|
if (!foundExport) {
|
||||||
error_setg(errp, "No export with name '%s' available", wantname);
|
error_setg(errp, "No export with name '%s' available", wantname);
|
||||||
|
nbd_send_opt_abort(ioc);
|
||||||
return -1;
|
return -1;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -352,12 +380,14 @@ static QIOChannel *nbd_receive_starttls(QIOChannel *ioc,
|
||||||
if (reply.type != NBD_REP_ACK) {
|
if (reply.type != NBD_REP_ACK) {
|
||||||
error_setg(errp, "Server rejected request to start TLS %" PRIx32,
|
error_setg(errp, "Server rejected request to start TLS %" PRIx32,
|
||||||
reply.type);
|
reply.type);
|
||||||
|
nbd_send_opt_abort(ioc);
|
||||||
return NULL;
|
return NULL;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (reply.length != 0) {
|
if (reply.length != 0) {
|
||||||
error_setg(errp, "Start TLS response was not zero %" PRIu32,
|
error_setg(errp, "Start TLS response was not zero %" PRIu32,
|
||||||
reply.length);
|
reply.length);
|
||||||
|
nbd_send_opt_abort(ioc);
|
||||||
return NULL;
|
return NULL;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
Loading…
Reference in a new issue