From nobody Fri May 03 00:15:42 2024 X-Original-To: dev-commits-src-main@mlmmj.nyi.freebsd.org Received: from mx1.freebsd.org (mx1.freebsd.org [IPv6:2610:1c1:1:606c::19:1]) by mlmmj.nyi.freebsd.org (Postfix) with ESMTP id 4VVrtR1t2qz5KJyG; Fri, 3 May 2024 00:15:43 +0000 (UTC) (envelope-from git@FreeBSD.org) Received: from mxrelay.nyi.freebsd.org (mxrelay.nyi.freebsd.org [IPv6:2610:1c1:1:606c::19:3]) (using TLSv1.3 with cipher TLS_AES_256_GCM_SHA384 (256/256 bits) key-exchange X25519 server-signature RSA-PSS (4096 bits) server-digest SHA256 client-signature RSA-PSS (4096 bits) client-digest SHA256) (Client CN "mxrelay.nyi.freebsd.org", Issuer "R3" (verified OK)) by mx1.freebsd.org (Postfix) with ESMTPS id 4VVrtQ6wFvz4QQK; Fri, 3 May 2024 00:15:42 +0000 (UTC) (envelope-from git@FreeBSD.org) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=freebsd.org; s=dkim; t=1714695343; h=from:from:reply-to:subject:subject:date:date:message-id:message-id: to:to:cc:mime-version:mime-version:content-type:content-type: content-transfer-encoding:content-transfer-encoding; bh=ZDZ1gH04tDONJkwABKOE8uckFwlQht2TdS6rgOJ+Wrg=; b=ewjq4kalU4+SyMNVwLH73M0dBPk1Y4gDiWllFr6eENkcvYwHuhtKZ/XC5ZVMhoHgaSFmts LIXuwmG0t5zC6XfhGDqsSyKvicKtp2gX/E6OXElQbcLws0K6IP1PaR45mjhFDXM4QWg+7G SKok8jXyui4AyMUS+YzZkjxKlFft8SaLQr96tVa5tA/mSXcD/aPUlHApOyXJdV5GSL78vi DJYQjiuth18d7HVVw7x5oo+EdQuDRzYOspFo8lJjzuo40kWaR20zkLVDhgZUrBhfm/MHHK 5JnS9PgMu2s7ZCNMQFhmFdBKVfhxTYrev1U49yYiNzoS8yaUAY3p2hDEmdXuyQ== ARC-Seal: i=1; s=dkim; d=freebsd.org; t=1714695343; a=rsa-sha256; cv=none; b=pGVwR91vhoZKamyHe+XkP5EgvMMDYz3cYvoXU0Vx7bEXflRBBTmnOARj23zAhU2Vc0b3ki tHXesWYyqUkzBPezpBBbbINpFPpY7/BwPkTWbS+AdUdytKMx/iMOYg+q6pGGayVyPQgkQS Mi60F6l9NnMdWC8k6ASdA62l2DkOAZsdtPSdF8g9D9Tt5qIu+M/SWPMRIZ6a24JKQVgogk Wen7QS1KoKw1wtDSYTW92wBdUgKz1p8mIQivbeUyrJIIUSG4VPXtTWxxG6NYl80CVeeUOS ZoLTsfuFCtLCOgMy5PcqqYom26q7q3ltCIyO4+moFi2i966Ko7V09ayuDx7bsg== ARC-Authentication-Results: i=1; mx1.freebsd.org; none ARC-Message-Signature: i=1; a=rsa-sha256; c=relaxed/relaxed; d=freebsd.org; s=dkim; t=1714695343; h=from:from:reply-to:subject:subject:date:date:message-id:message-id: to:to:cc:mime-version:mime-version:content-type:content-type: content-transfer-encoding:content-transfer-encoding; bh=ZDZ1gH04tDONJkwABKOE8uckFwlQht2TdS6rgOJ+Wrg=; b=jRndnACxqyzvqB0mr/mE5TUvHHw4xu6gGntGqDoFqSRKkJqYTIz1w2THIFScOw1/0pHU7o 9sj6qXNeTeolUqmxWzQ+J5OHXVFkvsoYF6gI701sSDm/cmv/fi+OUB3zYrw0thXK+NlfTy qvs6Gad43hmSVU0t+SFA8ywg0ErODGTQ3bRtthRnyaZj8DTRfgn4mFsenphoYmXX/5Du+W 7gJwtNfemyfJjrsX2nmIOE3h7EwD0QYciRDgdxZa97AjbEJHWZNqwuVyQxS7y/YqgnqMCx axPAhjLms/EJKEDJY1eqNRp9QjMb3z6zxLCnNdCD4O3MqnQUc0+FNbgnZwEowQ== Received: from gitrepo.freebsd.org (gitrepo.freebsd.org [IPv6:2610:1c1:1:6068::e6a:5]) (using TLSv1.3 with cipher TLS_AES_256_GCM_SHA384 (256/256 bits) key-exchange X25519 server-signature RSA-PSS (4096 bits) server-digest SHA256) (Client did not present a certificate) by mxrelay.nyi.freebsd.org (Postfix) with ESMTPS id 4VVrtQ6FRZzNxr; Fri, 3 May 2024 00:15:42 +0000 (UTC) (envelope-from git@FreeBSD.org) Received: from gitrepo.freebsd.org ([127.0.1.44]) by gitrepo.freebsd.org (8.17.1/8.17.1) with ESMTP id 4430FgNI078883; Fri, 3 May 2024 00:15:42 GMT (envelope-from git@gitrepo.freebsd.org) Received: (from git@localhost) by gitrepo.freebsd.org (8.17.1/8.17.1/Submit) id 4430Fggj078880; Fri, 3 May 2024 00:15:42 GMT (envelope-from git) Date: Fri, 3 May 2024 00:15:42 GMT Message-Id: <202405030015.4430Fggj078880@gitrepo.freebsd.org> To: src-committers@FreeBSD.org, dev-commits-src-all@FreeBSD.org, dev-commits-src-main@FreeBSD.org From: John Baldwin Subject: git: bbd5b6fe91ec - main - nvmf_tcp.h: Internal header shared between userspace and kernel List-Id: Commit messages for the main branch of the src repository List-Archive: https://lists.freebsd.org/archives/dev-commits-src-main List-Help: List-Post: List-Subscribe: List-Unsubscribe: X-BeenThere: dev-commits-src-main@freebsd.org Sender: owner-dev-commits-src-main@FreeBSD.org MIME-Version: 1.0 Content-Type: text/plain; charset=utf-8 Content-Transfer-Encoding: 8bit X-Git-Committer: jhb X-Git-Repository: src X-Git-Refname: refs/heads/main X-Git-Reftype: branch X-Git-Commit: bbd5b6fe91ecaf46d1104182acfbc7b398e9ca3d Auto-Submitted: auto-generated The branch main has been updated by jhb: URL: https://cgit.FreeBSD.org/src/commit/?id=bbd5b6fe91ecaf46d1104182acfbc7b398e9ca3d commit bbd5b6fe91ecaf46d1104182acfbc7b398e9ca3d Author: John Baldwin AuthorDate: 2024-05-02 23:27:38 +0000 Commit: John Baldwin CommitDate: 2024-05-02 23:27:38 +0000 nvmf_tcp.h: Internal header shared between userspace and kernel - Helper macros for specific SGL types used with the TCP transport - An inline function which validates various fields in TCP PDUs Reviewed by: imp Sponsored by: Chelsio Communications Differential Revision: https://reviews.freebsd.org/D44708 --- sys/dev/nvmf/nvmf_tcp.h | 276 ++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 276 insertions(+) diff --git a/sys/dev/nvmf/nvmf_tcp.h b/sys/dev/nvmf/nvmf_tcp.h new file mode 100644 index 000000000000..00b0917f75a4 --- /dev/null +++ b/sys/dev/nvmf/nvmf_tcp.h @@ -0,0 +1,276 @@ +/*- + * SPDX-License-Identifier: BSD-2-Clause + * + * Copyright (c) 2022-2024 Chelsio Communications, Inc. + * Written by: John Baldwin + */ + +#ifndef __NVMF_TCP_H__ +#define __NVMF_TCP_H__ + +#ifndef _KERNEL +#define __assert_unreachable __unreachable +#define MPASS assert +#endif + +#define NVME_SGL_TYPE_ICD \ + NVME_SGL_TYPE(NVME_SGL_TYPE_DATA_BLOCK, NVME_SGL_SUBTYPE_OFFSET) + +#define NVME_SGL_TYPE_COMMAND_BUFFER \ + NVME_SGL_TYPE(NVME_SGL_TYPE_TRANSPORT_DATA_BLOCK, \ + NVME_SGL_SUBTYPE_TRANSPORT) + +/* + * Validate common fields in a received PDU header. If an error is + * detected that requires an immediate disconnect, ECONNRESET is + * returned. If an error is detected that should be reported, EBADMSG + * is returned and *fes and *fei are set to the values to be used in a + * termination request PDU. If no error is detected, 0 is returned + * and *data_lenp is set to the length of any included data. + * + * Section number references refer to NVM Express over Fabrics + * Revision 1.1 dated October 22, 2019. + */ +static __inline int +nvmf_tcp_validate_pdu_header(const struct nvme_tcp_common_pdu_hdr *ch, + bool controller, bool header_digests, bool data_digests, uint8_t rxpda, + uint32_t *data_lenp, uint16_t *fes, uint32_t *fei) +{ + uint32_t data_len, plen; + u_int expected_hlen, full_hlen; + uint8_t digest_flags, valid_flags; + + plen = le32toh(ch->plen); + + /* + * Errors must be reported for the lowest incorrect field + * first, so validate fields in order. + */ + + /* Validate pdu_type. */ + + /* Controllers only receive PDUs with a PDU direction of 0. */ + if (controller != (ch->pdu_type & 0x01) == 0) { + printf("NVMe/TCP: Invalid PDU type %u\n", ch->pdu_type); + *fes = NVME_TCP_TERM_REQ_FES_INVALID_HEADER_FIELD; + *fei = offsetof(struct nvme_tcp_common_pdu_hdr, pdu_type); + return (EBADMSG); + } + + switch (ch->pdu_type) { + case NVME_TCP_PDU_TYPE_IC_REQ: + case NVME_TCP_PDU_TYPE_IC_RESP: + /* Shouldn't get these for an established connection. */ + printf("NVMe/TCP: Received Initialize Connection PDU\n"); + *fes = NVME_TCP_TERM_REQ_FES_INVALID_HEADER_FIELD; + *fei = offsetof(struct nvme_tcp_common_pdu_hdr, pdu_type); + return (EBADMSG); + case NVME_TCP_PDU_TYPE_H2C_TERM_REQ: + case NVME_TCP_PDU_TYPE_C2H_TERM_REQ: + /* + * 7.4.7 Termination requests with invalid PDU lengths + * result in an immediate connection termination + * without reporting an error. + */ + if (plen < sizeof(struct nvme_tcp_term_req_hdr) || + plen > NVME_TCP_TERM_REQ_PDU_MAX_SIZE) { + printf("NVMe/TCP: Received invalid termination request\n"); + return (ECONNRESET); + } + break; + case NVME_TCP_PDU_TYPE_CAPSULE_CMD: + case NVME_TCP_PDU_TYPE_CAPSULE_RESP: + case NVME_TCP_PDU_TYPE_H2C_DATA: + case NVME_TCP_PDU_TYPE_C2H_DATA: + case NVME_TCP_PDU_TYPE_R2T: + break; + default: + printf("NVMe/TCP: Invalid PDU type %u\n", ch->pdu_type); + *fes = NVME_TCP_TERM_REQ_FES_INVALID_HEADER_FIELD; + *fei = offsetof(struct nvme_tcp_common_pdu_hdr, pdu_type); + return (EBADMSG); + } + + /* Validate flags. */ + switch (ch->pdu_type) { + default: + __assert_unreachable(); + break; + case NVME_TCP_PDU_TYPE_H2C_TERM_REQ: + case NVME_TCP_PDU_TYPE_C2H_TERM_REQ: + valid_flags = 0; + break; + case NVME_TCP_PDU_TYPE_CAPSULE_CMD: + valid_flags = NVME_TCP_CH_FLAGS_HDGSTF | + NVME_TCP_CH_FLAGS_DDGSTF; + break; + case NVME_TCP_PDU_TYPE_CAPSULE_RESP: + case NVME_TCP_PDU_TYPE_R2T: + valid_flags = NVME_TCP_CH_FLAGS_HDGSTF; + break; + case NVME_TCP_PDU_TYPE_H2C_DATA: + valid_flags = NVME_TCP_CH_FLAGS_HDGSTF | + NVME_TCP_CH_FLAGS_DDGSTF | NVME_TCP_H2C_DATA_FLAGS_LAST_PDU; + break; + case NVME_TCP_PDU_TYPE_C2H_DATA: + valid_flags = NVME_TCP_CH_FLAGS_HDGSTF | + NVME_TCP_CH_FLAGS_DDGSTF | NVME_TCP_C2H_DATA_FLAGS_LAST_PDU | + NVME_TCP_C2H_DATA_FLAGS_SUCCESS; + break; + } + if ((ch->flags & ~valid_flags) != 0) { + printf("NVMe/TCP: Invalid PDU header flags %#x\n", ch->flags); + *fes = NVME_TCP_TERM_REQ_FES_INVALID_HEADER_FIELD; + *fei = offsetof(struct nvme_tcp_common_pdu_hdr, flags); + return (EBADMSG); + } + + /* Verify that digests are present iff enabled. */ + digest_flags = 0; + if (header_digests) + digest_flags |= NVME_TCP_CH_FLAGS_HDGSTF; + if (data_digests) + digest_flags |= NVME_TCP_CH_FLAGS_DDGSTF; + if ((digest_flags & valid_flags) != + (ch->flags & (NVME_TCP_CH_FLAGS_HDGSTF | + NVME_TCP_CH_FLAGS_DDGSTF))) { + printf("NVMe/TCP: Invalid PDU header flags %#x\n", ch->flags); + *fes = NVME_TCP_TERM_REQ_FES_INVALID_HEADER_FIELD; + *fei = offsetof(struct nvme_tcp_common_pdu_hdr, flags); + return (EBADMSG); + } + + /* 7.4.5.2: SUCCESS in C2H requires LAST_PDU */ + if (ch->pdu_type == NVME_TCP_PDU_TYPE_C2H_DATA && + (ch->flags & (NVME_TCP_C2H_DATA_FLAGS_LAST_PDU | + NVME_TCP_C2H_DATA_FLAGS_SUCCESS)) == + NVME_TCP_C2H_DATA_FLAGS_SUCCESS) { + printf("NVMe/TCP: Invalid PDU header flags %#x\n", ch->flags); + *fes = NVME_TCP_TERM_REQ_FES_INVALID_HEADER_FIELD; + *fei = offsetof(struct nvme_tcp_common_pdu_hdr, flags); + return (EBADMSG); + } + + /* Validate hlen. */ + switch (ch->pdu_type) { + default: + __assert_unreachable(); + break; + case NVME_TCP_PDU_TYPE_H2C_TERM_REQ: + case NVME_TCP_PDU_TYPE_C2H_TERM_REQ: + expected_hlen = sizeof(struct nvme_tcp_term_req_hdr); + break; + case NVME_TCP_PDU_TYPE_CAPSULE_CMD: + expected_hlen = sizeof(struct nvme_tcp_cmd); + break; + case NVME_TCP_PDU_TYPE_CAPSULE_RESP: + expected_hlen = sizeof(struct nvme_tcp_rsp); + break; + case NVME_TCP_PDU_TYPE_H2C_DATA: + expected_hlen = sizeof(struct nvme_tcp_h2c_data_hdr); + break; + case NVME_TCP_PDU_TYPE_C2H_DATA: + expected_hlen = sizeof(struct nvme_tcp_c2h_data_hdr); + break; + case NVME_TCP_PDU_TYPE_R2T: + expected_hlen = sizeof(struct nvme_tcp_r2t_hdr); + break; + } + if (ch->hlen != expected_hlen) { + printf("NVMe/TCP: Invalid PDU header length %u\n", ch->hlen); + *fes = NVME_TCP_TERM_REQ_FES_INVALID_HEADER_FIELD; + *fei = offsetof(struct nvme_tcp_common_pdu_hdr, hlen); + return (EBADMSG); + } + + /* Validate pdo. */ + full_hlen = ch->hlen; + if ((ch->flags & NVME_TCP_CH_FLAGS_HDGSTF) != 0) + full_hlen += sizeof(uint32_t); + switch (ch->pdu_type) { + default: + __assert_unreachable(); + break; + case NVME_TCP_PDU_TYPE_H2C_TERM_REQ: + case NVME_TCP_PDU_TYPE_C2H_TERM_REQ: + case NVME_TCP_PDU_TYPE_CAPSULE_RESP: + case NVME_TCP_PDU_TYPE_R2T: + if (ch->pdo != 0) { + printf("NVMe/TCP: Invalid PDU data offset %u\n", + ch->pdo); + *fes = NVME_TCP_TERM_REQ_FES_INVALID_HEADER_FIELD; + *fei = offsetof(struct nvme_tcp_common_pdu_hdr, pdo); + return (EBADMSG); + } + break; + case NVME_TCP_PDU_TYPE_CAPSULE_CMD: + case NVME_TCP_PDU_TYPE_H2C_DATA: + case NVME_TCP_PDU_TYPE_C2H_DATA: + /* Permit PDO of 0 if there is no data. */ + if (full_hlen == plen && ch->pdo == 0) + break; + + if (ch->pdo < full_hlen || ch->pdo > plen || + ch->pdo % rxpda != 0) { + printf("NVMe/TCP: Invalid PDU data offset %u\n", + ch->pdo); + *fes = NVME_TCP_TERM_REQ_FES_INVALID_HEADER_FIELD; + *fei = offsetof(struct nvme_tcp_common_pdu_hdr, pdo); + return (EBADMSG); + } + break; + } + + /* Validate plen. */ + if (plen < ch->hlen) { + printf("NVMe/TCP: Invalid PDU length %u\n", plen); + *fes = NVME_TCP_TERM_REQ_FES_INVALID_HEADER_FIELD; + *fei = offsetof(struct nvme_tcp_common_pdu_hdr, plen); + return (EBADMSG); + } + + if (plen == full_hlen) + data_len = 0; + else + data_len = plen - ch->pdo; + switch (ch->pdu_type) { + default: + __assert_unreachable(); + break; + case NVME_TCP_PDU_TYPE_H2C_TERM_REQ: + case NVME_TCP_PDU_TYPE_C2H_TERM_REQ: + /* Checked above. */ + MPASS(plen <= NVME_TCP_TERM_REQ_PDU_MAX_SIZE); + break; + case NVME_TCP_PDU_TYPE_CAPSULE_CMD: + case NVME_TCP_PDU_TYPE_H2C_DATA: + case NVME_TCP_PDU_TYPE_C2H_DATA: + if ((ch->flags & NVME_TCP_CH_FLAGS_DDGSTF) != 0 && + data_len <= sizeof(uint32_t)) { + printf("NVMe/TCP: PDU %u too short for digest\n", + ch->pdu_type); + *fes = NVME_TCP_TERM_REQ_FES_INVALID_HEADER_FIELD; + *fei = offsetof(struct nvme_tcp_common_pdu_hdr, plen); + return (EBADMSG); + } + break; + case NVME_TCP_PDU_TYPE_R2T: + case NVME_TCP_PDU_TYPE_CAPSULE_RESP: + if (data_len != 0) { + printf("NVMe/TCP: PDU %u with data length %u\n", + ch->pdu_type, data_len); + *fes = NVME_TCP_TERM_REQ_FES_INVALID_HEADER_FIELD; + *fei = offsetof(struct nvme_tcp_common_pdu_hdr, plen); + return (EBADMSG); + } + break; + } + + if ((ch->flags & NVME_TCP_CH_FLAGS_DDGSTF) != 0) + data_len -= sizeof(uint32_t); + + *data_lenp = data_len; + return (0); +} + +#endif /* !__NVMF_TCP_H__ */