From nobody Mon Feb 9 17:58:51 2026 Delivered-To: importer@patchew.org Received-SPF: pass (zoho.com: domain of gnu.org designates 208.118.235.17 as permitted sender) client-ip=208.118.235.17; envelope-from=qemu-devel-bounces+importer=patchew.org@nongnu.org; helo=lists.gnu.org; Authentication-Results: mx.zohomail.com; dkim=fail; spf=pass (zoho.com: domain of gnu.org designates 208.118.235.17 as permitted sender) smtp.mailfrom=qemu-devel-bounces+importer=patchew.org@nongnu.org Return-Path: Received: from lists.gnu.org (lists.gnu.org [208.118.235.17]) by mx.zohomail.com with SMTPS id 1499957282931669.4791943752693; Thu, 13 Jul 2017 07:48:02 -0700 (PDT) Received: from localhost ([::1]:60467 helo=lists.gnu.org) by lists.gnu.org with esmtp (Exim 4.71) (envelope-from ) id 1dVfPK-0005eI-H3 for importer@patchew.org; Thu, 13 Jul 2017 10:47:58 -0400 Received: from eggs.gnu.org ([2001:4830:134:3::10]:51531) by lists.gnu.org with esmtp (Exim 4.71) (envelope-from ) id 1dVf3n-0001Km-7Z for qemu-devel@nongnu.org; Thu, 13 Jul 2017 10:25:45 -0400 Received: from Debian-exim by eggs.gnu.org with spam-scanned (Exim 4.71) (envelope-from ) id 1dVf3j-0002qH-4P for qemu-devel@nongnu.org; Thu, 13 Jul 2017 10:25:43 -0400 Received: from mail-wm0-x241.google.com ([2a00:1450:400c:c09::241]:34932) by eggs.gnu.org with esmtps (TLS1.0:RSA_AES_128_CBC_SHA1:16) (Exim 4.71) (envelope-from ) id 1dVf3i-0002o8-JI for qemu-devel@nongnu.org; Thu, 13 Jul 2017 10:25:39 -0400 Received: by mail-wm0-x241.google.com with SMTP id u23so5459540wma.2 for ; Thu, 13 Jul 2017 07:25:38 -0700 (PDT) Received: from 640k.lan (94-39-191-51.adsl-ull.clienti.tiscali.it. [94.39.191.51]) by smtp.gmail.com with ESMTPSA id k75sm6042448wmh.10.2017.07.13.07.25.36 (version=TLS1_2 cipher=ECDHE-RSA-AES128-GCM-SHA256 bits=128/128); Thu, 13 Jul 2017 07:25:36 -0700 (PDT) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=gmail.com; s=20161025; h=sender:from:to:cc:subject:date:message-id:in-reply-to:references; bh=B8rYFVBgkNDL3inFPZBa37F9HmdSCUShPO7v3VDHqx4=; b=OEfRQwR+8OH627e/jNtUHmx9sNrPROQsvWxhs8D7Tq1zwidN+5a/CLfrtEf42EqE6j hrzSZMkTREOzaFW/2kAQS14/fBnkIAou8036BL/L/GSHeFvrmQPMvskj6w7aNA9d0pPD pQPVBWIgvaiTy+XoRRboX0EEFK3HHBCdkulCH79cAoMR7KE7juFyu/4PKScC9rj5H13y FlEWAwaK6ktfjHqbH7pfIYVE2dhhzb544HGYgZM1Tjkr57/SrTCTbhQ+BLGH/nczrt5I L0WHkld3iGn2jhsbr8oqE1Lf5xgkxvU0coPlpcqirwHksL4CDYX/VNEqVyhzZ5gQ+ZzQ 51KQ== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20161025; h=x-gm-message-state:sender:from:to:cc:subject:date:message-id :in-reply-to:references; bh=B8rYFVBgkNDL3inFPZBa37F9HmdSCUShPO7v3VDHqx4=; b=t8zWc+G7ELcQW+ZH6vOxr71qR1QDxC4BfjUhd8w0MINqfJTaYEsONRhISPfK9oXGwU fA5DXAwAV3yfU6JSNAb5YkP7izahFYS/wOiNdXoMEPeVYaKNzBEq+lgPl/MAdDsY1KhV +D/N0yV/ueGbKF8kcT2qP4RLy6R7h+XOz6Qe9aKB50IxEuQP1W2BYeMvDyXE0ckEZThd FqzQAjZRULDS8gafgvlgf6smCKzt+GICAj15TftcRFf0eq4kxt8u6carR+eBnk7LWIbn 72Hf+pgJj6LmjSrAvhGM8em6+6rrvtno1QEjoqcGajX5WNcSA6ht6VDYboSG6ZECBv81 HGew== X-Gm-Message-State: AIVw1126qRIyGflGq17iyFiuuzJDlR+zJxQ8Gv4VkNERUkuYBxBBL85B U7NaPQ6F5oRWko4cSdc= X-Received: by 10.28.113.21 with SMTP id m21mr2286272wmc.80.1499955937239; Thu, 13 Jul 2017 07:25:37 -0700 (PDT) From: Paolo Bonzini To: qemu-devel@nongnu.org Date: Thu, 13 Jul 2017 16:24:29 +0200 Message-Id: <1499955874-10954-37-git-send-email-pbonzini@redhat.com> X-Mailer: git-send-email 1.8.3.1 In-Reply-To: <1499955874-10954-1-git-send-email-pbonzini@redhat.com> References: <1499955874-10954-1-git-send-email-pbonzini@redhat.com> X-detected-operating-system: by eggs.gnu.org: Genre and OS details not recognized. X-Received-From: 2a00:1450:400c:c09::241 Subject: [Qemu-devel] [PULL 36/41] nbd: Implement NBD_OPT_GO on server X-BeenThere: qemu-devel@nongnu.org X-Mailman-Version: 2.1.21 Precedence: list List-Id: List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , Errors-To: qemu-devel-bounces+importer=patchew.org@nongnu.org Sender: "Qemu-devel" X-ZohoMail-DKIM: fail (Header signature does not verify) X-ZohoMail: RDKM_2 RSF_0 Z_629925259 SPT_0 Content-Transfer-Encoding: quoted-printable MIME-Version: 1.0 Content-Type: text/plain; charset="utf-8" From: Eric Blake NBD_OPT_EXPORT_NAME is lousy: per the NBD protocol, any failure requires us to close the connection rather than report an error. Therefore, upstream NBD recently added NBD_OPT_GO as the improved version of the option that does what we want [1], along with NBD_OPT_INFO that returns the same information but does not transition to transmission phase. [1] https://github.com/NetworkBlockDevice/nbd/blob/extension-info/doc/proto= .md This is a first cut at the information types, and only passes the same information already available through NBD_OPT_LIST and NBD_OPT_EXPORT_NAME; items like NBD_INFO_BLOCK_SIZE (and thus any use of NBD_REP_ERR_BLOCK_SIZE_REQD) are intentionally left for later patches. Signed-off-by: Eric Blake Message-Id: <20170707203049.534-7-eblake@redhat.com> Signed-off-by: Paolo Bonzini --- nbd/server.c | 179 +++++++++++++++++++++++++++++++++++++++++++++++++++= +++- nbd/trace-events | 3 + 2 files changed, 179 insertions(+), 3 deletions(-) diff --git a/nbd/server.c b/nbd/server.c index 841986d..e84d012 100644 --- a/nbd/server.c +++ b/nbd/server.c @@ -141,6 +141,7 @@ static int nbd_negotiate_send_rep_len(QIOChannel *ioc, = uint32_t type, trace_nbd_negotiate_send_rep_len(opt, nbd_opt_lookup(opt), type, nbd_rep_lookup(type), len); =20 + assert(len < NBD_MAX_BUFFER_SIZE); magic =3D cpu_to_be64(NBD_REP_MAGIC); if (nbd_write(ioc, &magic, sizeof(magic), errp) < 0) { error_prepend(errp, "write failed (rep magic): "); @@ -275,6 +276,8 @@ static int nbd_negotiate_handle_list(NBDClient *client,= uint32_t length, return nbd_negotiate_send_rep(client->ioc, NBD_REP_ACK, NBD_OPT_LIST, = errp); } =20 +/* Send a reply to NBD_OPT_EXPORT_NAME. + * Return -errno on error, 0 on success. */ static int nbd_negotiate_handle_export_name(NBDClient *client, uint32_t le= ngth, uint16_t myflags, bool no_zero= es, Error **errp) @@ -323,6 +326,162 @@ static int nbd_negotiate_handle_export_name(NBDClient= *client, uint32_t length, return 0; } =20 +/* Send a single NBD_REP_INFO, with a buffer @buf of @length bytes. + * The buffer does NOT include the info type prefix. + * Return -errno on error, 0 if ready to send more. */ +static int nbd_negotiate_send_info(NBDClient *client, uint32_t opt, + uint16_t info, uint32_t length, void *b= uf, + Error **errp) +{ + int rc; + + trace_nbd_negotiate_send_info(info, nbd_info_lookup(info), length); + rc =3D nbd_negotiate_send_rep_len(client->ioc, NBD_REP_INFO, opt, + sizeof(info) + length, errp); + if (rc < 0) { + return rc; + } + cpu_to_be16s(&info); + if (nbd_write(client->ioc, &info, sizeof(info), errp) < 0) { + return -EIO; + } + if (nbd_write(client->ioc, buf, length, errp) < 0) { + return -EIO; + } + return 0; +} + +/* Handle NBD_OPT_INFO and NBD_OPT_GO. + * Return -errno on error, 0 if ready for next option, and 1 to move + * into transmission phase. */ +static int nbd_negotiate_handle_info(NBDClient *client, uint32_t length, + uint32_t opt, uint16_t myflags, + Error **errp) +{ + int rc; + char name[NBD_MAX_NAME_SIZE + 1]; + NBDExport *exp; + uint16_t requests; + uint16_t request; + uint32_t namelen; + bool sendname =3D false; + char buf[sizeof(uint64_t) + sizeof(uint16_t)]; + const char *msg; + + /* Client sends: + 4 bytes: L, name length (can be 0) + L bytes: export name + 2 bytes: N, number of requests (can be 0) + N * 2 bytes: N requests + */ + if (length < sizeof(namelen) + sizeof(requests)) { + msg =3D "overall request too short"; + goto invalid; + } + if (nbd_read(client->ioc, &namelen, sizeof(namelen), errp) < 0) { + return -EIO; + } + be32_to_cpus(&namelen); + length -=3D sizeof(namelen); + if (namelen > length - sizeof(requests) || (length - namelen) % 2) { + msg =3D "name length is incorrect"; + goto invalid; + } + if (nbd_read(client->ioc, name, namelen, errp) < 0) { + return -EIO; + } + name[namelen] =3D '\0'; + length -=3D namelen; + trace_nbd_negotiate_handle_export_name_request(name); + + if (nbd_read(client->ioc, &requests, sizeof(requests), errp) < 0) { + return -EIO; + } + be16_to_cpus(&requests); + length -=3D sizeof(requests); + trace_nbd_negotiate_handle_info_requests(requests); + if (requests !=3D length / sizeof(request)) { + msg =3D "incorrect number of requests for overall length"; + goto invalid; + } + while (requests--) { + if (nbd_read(client->ioc, &request, sizeof(request), errp) < 0) { + return -EIO; + } + be16_to_cpus(&request); + length -=3D sizeof(request); + trace_nbd_negotiate_handle_info_request(request, + nbd_info_lookup(request)); + /* For now, we only care about NBD_INFO_NAME; everything else + * is either a request we don't know or something we send + * regardless of request. */ + if (request =3D=3D NBD_INFO_NAME) { + sendname =3D true; + } + } + + exp =3D nbd_export_find(name); + if (!exp) { + return nbd_negotiate_send_rep_err(client->ioc, NBD_REP_ERR_UNKNOWN, + opt, errp, "export '%s' not pres= ent", + name); + } + + /* Don't bother sending NBD_INFO_NAME unless client requested it */ + if (sendname) { + rc =3D nbd_negotiate_send_info(client, opt, NBD_INFO_NAME, length,= name, + errp); + if (rc < 0) { + return rc; + } + } + + /* Send NBD_INFO_DESCRIPTION only if available, regardless of + * client request */ + if (exp->description) { + size_t len =3D strlen(exp->description); + + rc =3D nbd_negotiate_send_info(client, opt, NBD_INFO_DESCRIPTION, + len, exp->description, errp); + if (rc < 0) { + return rc; + } + } + + /* Send NBD_INFO_EXPORT always */ + trace_nbd_negotiate_new_style_size_flags(exp->size, + exp->nbdflags | myflags); + stq_be_p(buf, exp->size); + stw_be_p(buf + 8, exp->nbdflags | myflags); + rc =3D nbd_negotiate_send_info(client, opt, NBD_INFO_EXPORT, + sizeof(buf), buf, errp); + if (rc < 0) { + return rc; + } + + /* Final reply */ + rc =3D nbd_negotiate_send_rep(client->ioc, NBD_REP_ACK, opt, errp); + if (rc < 0) { + return rc; + } + + if (opt =3D=3D NBD_OPT_GO) { + client->exp =3D exp; + QTAILQ_INSERT_TAIL(&client->exp->clients, client, next); + nbd_export_get(client->exp); + rc =3D 1; + } + return rc; + + invalid: + if (nbd_drop(client->ioc, length, errp) < 0) { + return -EIO; + } + return nbd_negotiate_send_rep_err(client->ioc, NBD_REP_ERR_INVALID, op= t, + errp, "%s", msg); +} + + /* Handle NBD_OPT_STARTTLS. Return NULL to drop connection, or else the * new channel for all further (now-encrypted) communication. */ static QIOChannel *nbd_negotiate_handle_starttls(NBDClient *client, @@ -380,7 +539,8 @@ static QIOChannel *nbd_negotiate_handle_starttls(NBDCli= ent *client, } =20 /* nbd_negotiate_options - * Process all NBD_OPT_* client option commands. + * Process all NBD_OPT_* client option commands, during fixed newstyle + * negotiation. * Return: * -errno on error, errp is set * 0 on successful negotiation, errp is not set @@ -397,7 +557,7 @@ static int nbd_negotiate_options(NBDClient *client, uin= t16_t myflags, /* Client sends: [ 0 .. 3] client flags =20 - Then we loop until NBD_OPT_EXPORT_NAME: + Then we loop until NBD_OPT_EXPORT_NAME or NBD_OPT_GO: [ 0 .. 7] NBD_OPTS_MAGIC [ 8 .. 11] NBD option [12 .. 15] Data length @@ -524,6 +684,19 @@ static int nbd_negotiate_options(NBDClient *client, ui= nt16_t myflags, myflags, no_zeroes, errp); =20 + case NBD_OPT_INFO: + case NBD_OPT_GO: + ret =3D nbd_negotiate_handle_info(client, length, option, + myflags, errp); + if (ret =3D=3D 1) { + assert(option =3D=3D NBD_OPT_GO); + return 0; + } + if (ret) { + return ret; + } + break; + case NBD_OPT_STARTTLS: if (nbd_drop(client->ioc, length, errp) < 0) { return -EIO; @@ -606,7 +779,7 @@ static coroutine_fn int nbd_negotiate(NBDClient *client= , Error **errp) [ 0 .. 7] passwd ("NBDMAGIC") [ 8 .. 15] magic (NBD_OPTS_MAGIC) [16 .. 17] server flags (0) - ....options sent, ending in NBD_OPT_EXPORT_NAME.... + ....options sent, ending in NBD_OPT_EXPORT_NAME or NBD_OPT_GO.... */ =20 qio_channel_set_blocking(client->ioc, false, NULL); diff --git a/nbd/trace-events b/nbd/trace-events index 8db0500..5230c61 100644 --- a/nbd/trace-events +++ b/nbd/trace-events @@ -33,6 +33,9 @@ nbd_negotiate_send_rep_err(const char *msg) "sending erro= r message \"%s\"" nbd_negotiate_send_rep_list(const char *name, const char *desc) "Advertisi= ng export name '%s' description '%s'" nbd_negotiate_handle_export_name(void) "Checking length" nbd_negotiate_handle_export_name_request(const char *name) "Client request= ed export '%s'" +nbd_negotiate_send_info(int info, const char *name, uint32_t length) "Send= ing NBD_REP_INFO type %d (%s) with remaining length %" PRIu32 +nbd_negotiate_handle_info_requests(int requests) "Client requested %d item= s of info" +nbd_negotiate_handle_info_request(int request, const char *name) "Client r= equested info %d (%s)" nbd_negotiate_handle_starttls(void) "Setting up TLS" nbd_negotiate_handle_starttls_handshake(void) "Starting TLS handshake" nbd_negotiate_options_flags(uint32_t flags) "Received client flags 0x%" PR= Ix32 --=20 1.8.3.1