1bbd5b6feSJohn Baldwin /*- 2bbd5b6feSJohn Baldwin * SPDX-License-Identifier: BSD-2-Clause 3bbd5b6feSJohn Baldwin * 4bbd5b6feSJohn Baldwin * Copyright (c) 2022-2024 Chelsio Communications, Inc. 5bbd5b6feSJohn Baldwin * Written by: John Baldwin <jhb@FreeBSD.org> 6bbd5b6feSJohn Baldwin */ 7bbd5b6feSJohn Baldwin 8bbd5b6feSJohn Baldwin #ifndef __NVMF_TCP_H__ 9bbd5b6feSJohn Baldwin #define __NVMF_TCP_H__ 10bbd5b6feSJohn Baldwin 11bbd5b6feSJohn Baldwin #ifndef _KERNEL 12bbd5b6feSJohn Baldwin #define MPASS assert 13bbd5b6feSJohn Baldwin #endif 14bbd5b6feSJohn Baldwin 15bbd5b6feSJohn Baldwin #define NVME_SGL_TYPE_ICD \ 16bbd5b6feSJohn Baldwin NVME_SGL_TYPE(NVME_SGL_TYPE_DATA_BLOCK, NVME_SGL_SUBTYPE_OFFSET) 17bbd5b6feSJohn Baldwin 18bbd5b6feSJohn Baldwin #define NVME_SGL_TYPE_COMMAND_BUFFER \ 19bbd5b6feSJohn Baldwin NVME_SGL_TYPE(NVME_SGL_TYPE_TRANSPORT_DATA_BLOCK, \ 20bbd5b6feSJohn Baldwin NVME_SGL_SUBTYPE_TRANSPORT) 21bbd5b6feSJohn Baldwin 22bbd5b6feSJohn Baldwin /* 23bbd5b6feSJohn Baldwin * Validate common fields in a received PDU header. If an error is 24bbd5b6feSJohn Baldwin * detected that requires an immediate disconnect, ECONNRESET is 25bbd5b6feSJohn Baldwin * returned. If an error is detected that should be reported, EBADMSG 26bbd5b6feSJohn Baldwin * is returned and *fes and *fei are set to the values to be used in a 27bbd5b6feSJohn Baldwin * termination request PDU. If no error is detected, 0 is returned 28bbd5b6feSJohn Baldwin * and *data_lenp is set to the length of any included data. 29bbd5b6feSJohn Baldwin * 30bbd5b6feSJohn Baldwin * Section number references refer to NVM Express over Fabrics 31bbd5b6feSJohn Baldwin * Revision 1.1 dated October 22, 2019. 32bbd5b6feSJohn Baldwin */ 33bbd5b6feSJohn Baldwin static __inline int 34bbd5b6feSJohn Baldwin nvmf_tcp_validate_pdu_header(const struct nvme_tcp_common_pdu_hdr *ch, 35bbd5b6feSJohn Baldwin bool controller, bool header_digests, bool data_digests, uint8_t rxpda, 36bbd5b6feSJohn Baldwin uint32_t *data_lenp, uint16_t *fes, uint32_t *fei) 37bbd5b6feSJohn Baldwin { 38bbd5b6feSJohn Baldwin uint32_t data_len, plen; 39bbd5b6feSJohn Baldwin u_int expected_hlen, full_hlen; 40bbd5b6feSJohn Baldwin uint8_t digest_flags, valid_flags; 41bbd5b6feSJohn Baldwin 42bbd5b6feSJohn Baldwin plen = le32toh(ch->plen); 43*43d45f26SJohn Baldwin full_hlen = ch->hlen; 44*43d45f26SJohn Baldwin if ((ch->flags & NVME_TCP_CH_FLAGS_HDGSTF) != 0) 45*43d45f26SJohn Baldwin full_hlen += sizeof(uint32_t); 46*43d45f26SJohn Baldwin if (plen == full_hlen) 47*43d45f26SJohn Baldwin data_len = 0; 48*43d45f26SJohn Baldwin else 49*43d45f26SJohn Baldwin data_len = plen - ch->pdo; 50bbd5b6feSJohn Baldwin 51bbd5b6feSJohn Baldwin /* 52bbd5b6feSJohn Baldwin * Errors must be reported for the lowest incorrect field 53bbd5b6feSJohn Baldwin * first, so validate fields in order. 54bbd5b6feSJohn Baldwin */ 55bbd5b6feSJohn Baldwin 56bbd5b6feSJohn Baldwin /* Validate pdu_type. */ 57bbd5b6feSJohn Baldwin 58bbd5b6feSJohn Baldwin /* Controllers only receive PDUs with a PDU direction of 0. */ 59a7db82cfSJohn Baldwin if (controller != ((ch->pdu_type & 0x01) == 0)) { 60bbd5b6feSJohn Baldwin printf("NVMe/TCP: Invalid PDU type %u\n", ch->pdu_type); 61bbd5b6feSJohn Baldwin *fes = NVME_TCP_TERM_REQ_FES_INVALID_HEADER_FIELD; 62bbd5b6feSJohn Baldwin *fei = offsetof(struct nvme_tcp_common_pdu_hdr, pdu_type); 63bbd5b6feSJohn Baldwin return (EBADMSG); 64bbd5b6feSJohn Baldwin } 65bbd5b6feSJohn Baldwin 66bbd5b6feSJohn Baldwin switch (ch->pdu_type) { 67bbd5b6feSJohn Baldwin case NVME_TCP_PDU_TYPE_IC_REQ: 68bbd5b6feSJohn Baldwin case NVME_TCP_PDU_TYPE_IC_RESP: 69bbd5b6feSJohn Baldwin /* Shouldn't get these for an established connection. */ 70bbd5b6feSJohn Baldwin printf("NVMe/TCP: Received Initialize Connection PDU\n"); 71bbd5b6feSJohn Baldwin *fes = NVME_TCP_TERM_REQ_FES_INVALID_HEADER_FIELD; 72bbd5b6feSJohn Baldwin *fei = offsetof(struct nvme_tcp_common_pdu_hdr, pdu_type); 73bbd5b6feSJohn Baldwin return (EBADMSG); 74bbd5b6feSJohn Baldwin case NVME_TCP_PDU_TYPE_H2C_TERM_REQ: 75bbd5b6feSJohn Baldwin case NVME_TCP_PDU_TYPE_C2H_TERM_REQ: 76bbd5b6feSJohn Baldwin /* 77bbd5b6feSJohn Baldwin * 7.4.7 Termination requests with invalid PDU lengths 78bbd5b6feSJohn Baldwin * result in an immediate connection termination 79bbd5b6feSJohn Baldwin * without reporting an error. 80bbd5b6feSJohn Baldwin */ 81bbd5b6feSJohn Baldwin if (plen < sizeof(struct nvme_tcp_term_req_hdr) || 82bbd5b6feSJohn Baldwin plen > NVME_TCP_TERM_REQ_PDU_MAX_SIZE) { 83bbd5b6feSJohn Baldwin printf("NVMe/TCP: Received invalid termination request\n"); 84bbd5b6feSJohn Baldwin return (ECONNRESET); 85bbd5b6feSJohn Baldwin } 86bbd5b6feSJohn Baldwin break; 87bbd5b6feSJohn Baldwin case NVME_TCP_PDU_TYPE_CAPSULE_CMD: 88bbd5b6feSJohn Baldwin case NVME_TCP_PDU_TYPE_CAPSULE_RESP: 89bbd5b6feSJohn Baldwin case NVME_TCP_PDU_TYPE_H2C_DATA: 90bbd5b6feSJohn Baldwin case NVME_TCP_PDU_TYPE_C2H_DATA: 91bbd5b6feSJohn Baldwin case NVME_TCP_PDU_TYPE_R2T: 92bbd5b6feSJohn Baldwin break; 93bbd5b6feSJohn Baldwin default: 94bbd5b6feSJohn Baldwin printf("NVMe/TCP: Invalid PDU type %u\n", ch->pdu_type); 95bbd5b6feSJohn Baldwin *fes = NVME_TCP_TERM_REQ_FES_INVALID_HEADER_FIELD; 96bbd5b6feSJohn Baldwin *fei = offsetof(struct nvme_tcp_common_pdu_hdr, pdu_type); 97bbd5b6feSJohn Baldwin return (EBADMSG); 98bbd5b6feSJohn Baldwin } 99bbd5b6feSJohn Baldwin 100bbd5b6feSJohn Baldwin /* Validate flags. */ 101bbd5b6feSJohn Baldwin switch (ch->pdu_type) { 102bbd5b6feSJohn Baldwin default: 103bbd5b6feSJohn Baldwin __assert_unreachable(); 104bbd5b6feSJohn Baldwin break; 105bbd5b6feSJohn Baldwin case NVME_TCP_PDU_TYPE_H2C_TERM_REQ: 106bbd5b6feSJohn Baldwin case NVME_TCP_PDU_TYPE_C2H_TERM_REQ: 107bbd5b6feSJohn Baldwin valid_flags = 0; 108bbd5b6feSJohn Baldwin break; 109bbd5b6feSJohn Baldwin case NVME_TCP_PDU_TYPE_CAPSULE_CMD: 110bbd5b6feSJohn Baldwin valid_flags = NVME_TCP_CH_FLAGS_HDGSTF | 111bbd5b6feSJohn Baldwin NVME_TCP_CH_FLAGS_DDGSTF; 112bbd5b6feSJohn Baldwin break; 113bbd5b6feSJohn Baldwin case NVME_TCP_PDU_TYPE_CAPSULE_RESP: 114bbd5b6feSJohn Baldwin case NVME_TCP_PDU_TYPE_R2T: 115bbd5b6feSJohn Baldwin valid_flags = NVME_TCP_CH_FLAGS_HDGSTF; 116bbd5b6feSJohn Baldwin break; 117bbd5b6feSJohn Baldwin case NVME_TCP_PDU_TYPE_H2C_DATA: 118bbd5b6feSJohn Baldwin valid_flags = NVME_TCP_CH_FLAGS_HDGSTF | 119bbd5b6feSJohn Baldwin NVME_TCP_CH_FLAGS_DDGSTF | NVME_TCP_H2C_DATA_FLAGS_LAST_PDU; 120bbd5b6feSJohn Baldwin break; 121bbd5b6feSJohn Baldwin case NVME_TCP_PDU_TYPE_C2H_DATA: 122bbd5b6feSJohn Baldwin valid_flags = NVME_TCP_CH_FLAGS_HDGSTF | 123bbd5b6feSJohn Baldwin NVME_TCP_CH_FLAGS_DDGSTF | NVME_TCP_C2H_DATA_FLAGS_LAST_PDU | 124bbd5b6feSJohn Baldwin NVME_TCP_C2H_DATA_FLAGS_SUCCESS; 125bbd5b6feSJohn Baldwin break; 126bbd5b6feSJohn Baldwin } 127bbd5b6feSJohn Baldwin if ((ch->flags & ~valid_flags) != 0) { 128bbd5b6feSJohn Baldwin printf("NVMe/TCP: Invalid PDU header flags %#x\n", ch->flags); 129bbd5b6feSJohn Baldwin *fes = NVME_TCP_TERM_REQ_FES_INVALID_HEADER_FIELD; 130bbd5b6feSJohn Baldwin *fei = offsetof(struct nvme_tcp_common_pdu_hdr, flags); 131bbd5b6feSJohn Baldwin return (EBADMSG); 132bbd5b6feSJohn Baldwin } 133bbd5b6feSJohn Baldwin 134*43d45f26SJohn Baldwin /* 135*43d45f26SJohn Baldwin * Verify that digests are present iff enabled. Note that the 136*43d45f26SJohn Baldwin * data digest will not be present if there is no data 137*43d45f26SJohn Baldwin * payload. 138*43d45f26SJohn Baldwin */ 139bbd5b6feSJohn Baldwin digest_flags = 0; 140bbd5b6feSJohn Baldwin if (header_digests) 141bbd5b6feSJohn Baldwin digest_flags |= NVME_TCP_CH_FLAGS_HDGSTF; 142*43d45f26SJohn Baldwin if (data_digests && data_len != 0) 143bbd5b6feSJohn Baldwin digest_flags |= NVME_TCP_CH_FLAGS_DDGSTF; 144bbd5b6feSJohn Baldwin if ((digest_flags & valid_flags) != 145bbd5b6feSJohn Baldwin (ch->flags & (NVME_TCP_CH_FLAGS_HDGSTF | 146bbd5b6feSJohn Baldwin NVME_TCP_CH_FLAGS_DDGSTF))) { 147bbd5b6feSJohn Baldwin printf("NVMe/TCP: Invalid PDU header flags %#x\n", ch->flags); 148bbd5b6feSJohn Baldwin *fes = NVME_TCP_TERM_REQ_FES_INVALID_HEADER_FIELD; 149bbd5b6feSJohn Baldwin *fei = offsetof(struct nvme_tcp_common_pdu_hdr, flags); 150bbd5b6feSJohn Baldwin return (EBADMSG); 151bbd5b6feSJohn Baldwin } 152bbd5b6feSJohn Baldwin 153bbd5b6feSJohn Baldwin /* 7.4.5.2: SUCCESS in C2H requires LAST_PDU */ 154bbd5b6feSJohn Baldwin if (ch->pdu_type == NVME_TCP_PDU_TYPE_C2H_DATA && 155bbd5b6feSJohn Baldwin (ch->flags & (NVME_TCP_C2H_DATA_FLAGS_LAST_PDU | 156bbd5b6feSJohn Baldwin NVME_TCP_C2H_DATA_FLAGS_SUCCESS)) == 157bbd5b6feSJohn Baldwin NVME_TCP_C2H_DATA_FLAGS_SUCCESS) { 158bbd5b6feSJohn Baldwin printf("NVMe/TCP: Invalid PDU header flags %#x\n", ch->flags); 159bbd5b6feSJohn Baldwin *fes = NVME_TCP_TERM_REQ_FES_INVALID_HEADER_FIELD; 160bbd5b6feSJohn Baldwin *fei = offsetof(struct nvme_tcp_common_pdu_hdr, flags); 161bbd5b6feSJohn Baldwin return (EBADMSG); 162bbd5b6feSJohn Baldwin } 163bbd5b6feSJohn Baldwin 164bbd5b6feSJohn Baldwin /* Validate hlen. */ 165bbd5b6feSJohn Baldwin switch (ch->pdu_type) { 166bbd5b6feSJohn Baldwin default: 167bbd5b6feSJohn Baldwin __assert_unreachable(); 168bbd5b6feSJohn Baldwin break; 169bbd5b6feSJohn Baldwin case NVME_TCP_PDU_TYPE_H2C_TERM_REQ: 170bbd5b6feSJohn Baldwin case NVME_TCP_PDU_TYPE_C2H_TERM_REQ: 171bbd5b6feSJohn Baldwin expected_hlen = sizeof(struct nvme_tcp_term_req_hdr); 172bbd5b6feSJohn Baldwin break; 173bbd5b6feSJohn Baldwin case NVME_TCP_PDU_TYPE_CAPSULE_CMD: 174bbd5b6feSJohn Baldwin expected_hlen = sizeof(struct nvme_tcp_cmd); 175bbd5b6feSJohn Baldwin break; 176bbd5b6feSJohn Baldwin case NVME_TCP_PDU_TYPE_CAPSULE_RESP: 177bbd5b6feSJohn Baldwin expected_hlen = sizeof(struct nvme_tcp_rsp); 178bbd5b6feSJohn Baldwin break; 179bbd5b6feSJohn Baldwin case NVME_TCP_PDU_TYPE_H2C_DATA: 180bbd5b6feSJohn Baldwin expected_hlen = sizeof(struct nvme_tcp_h2c_data_hdr); 181bbd5b6feSJohn Baldwin break; 182bbd5b6feSJohn Baldwin case NVME_TCP_PDU_TYPE_C2H_DATA: 183bbd5b6feSJohn Baldwin expected_hlen = sizeof(struct nvme_tcp_c2h_data_hdr); 184bbd5b6feSJohn Baldwin break; 185bbd5b6feSJohn Baldwin case NVME_TCP_PDU_TYPE_R2T: 186bbd5b6feSJohn Baldwin expected_hlen = sizeof(struct nvme_tcp_r2t_hdr); 187bbd5b6feSJohn Baldwin break; 188bbd5b6feSJohn Baldwin } 189bbd5b6feSJohn Baldwin if (ch->hlen != expected_hlen) { 190bbd5b6feSJohn Baldwin printf("NVMe/TCP: Invalid PDU header length %u\n", ch->hlen); 191bbd5b6feSJohn Baldwin *fes = NVME_TCP_TERM_REQ_FES_INVALID_HEADER_FIELD; 192bbd5b6feSJohn Baldwin *fei = offsetof(struct nvme_tcp_common_pdu_hdr, hlen); 193bbd5b6feSJohn Baldwin return (EBADMSG); 194bbd5b6feSJohn Baldwin } 195bbd5b6feSJohn Baldwin 196bbd5b6feSJohn Baldwin /* Validate pdo. */ 197bbd5b6feSJohn Baldwin switch (ch->pdu_type) { 198bbd5b6feSJohn Baldwin default: 199bbd5b6feSJohn Baldwin __assert_unreachable(); 200bbd5b6feSJohn Baldwin break; 201bbd5b6feSJohn Baldwin case NVME_TCP_PDU_TYPE_H2C_TERM_REQ: 202bbd5b6feSJohn Baldwin case NVME_TCP_PDU_TYPE_C2H_TERM_REQ: 203bbd5b6feSJohn Baldwin case NVME_TCP_PDU_TYPE_CAPSULE_RESP: 204bbd5b6feSJohn Baldwin case NVME_TCP_PDU_TYPE_R2T: 205bbd5b6feSJohn Baldwin if (ch->pdo != 0) { 206bbd5b6feSJohn Baldwin printf("NVMe/TCP: Invalid PDU data offset %u\n", 207bbd5b6feSJohn Baldwin ch->pdo); 208bbd5b6feSJohn Baldwin *fes = NVME_TCP_TERM_REQ_FES_INVALID_HEADER_FIELD; 209bbd5b6feSJohn Baldwin *fei = offsetof(struct nvme_tcp_common_pdu_hdr, pdo); 210bbd5b6feSJohn Baldwin return (EBADMSG); 211bbd5b6feSJohn Baldwin } 212bbd5b6feSJohn Baldwin break; 213bbd5b6feSJohn Baldwin case NVME_TCP_PDU_TYPE_CAPSULE_CMD: 214bbd5b6feSJohn Baldwin case NVME_TCP_PDU_TYPE_H2C_DATA: 215bbd5b6feSJohn Baldwin case NVME_TCP_PDU_TYPE_C2H_DATA: 216bbd5b6feSJohn Baldwin /* Permit PDO of 0 if there is no data. */ 217*43d45f26SJohn Baldwin if (data_len == 0 && ch->pdo == 0) 218bbd5b6feSJohn Baldwin break; 219bbd5b6feSJohn Baldwin 220bbd5b6feSJohn Baldwin if (ch->pdo < full_hlen || ch->pdo > plen || 221bbd5b6feSJohn Baldwin ch->pdo % rxpda != 0) { 222bbd5b6feSJohn Baldwin printf("NVMe/TCP: Invalid PDU data offset %u\n", 223bbd5b6feSJohn Baldwin ch->pdo); 224bbd5b6feSJohn Baldwin *fes = NVME_TCP_TERM_REQ_FES_INVALID_HEADER_FIELD; 225bbd5b6feSJohn Baldwin *fei = offsetof(struct nvme_tcp_common_pdu_hdr, pdo); 226bbd5b6feSJohn Baldwin return (EBADMSG); 227bbd5b6feSJohn Baldwin } 228bbd5b6feSJohn Baldwin break; 229bbd5b6feSJohn Baldwin } 230bbd5b6feSJohn Baldwin 231bbd5b6feSJohn Baldwin /* Validate plen. */ 232bbd5b6feSJohn Baldwin if (plen < ch->hlen) { 233bbd5b6feSJohn Baldwin printf("NVMe/TCP: Invalid PDU length %u\n", plen); 234bbd5b6feSJohn Baldwin *fes = NVME_TCP_TERM_REQ_FES_INVALID_HEADER_FIELD; 235bbd5b6feSJohn Baldwin *fei = offsetof(struct nvme_tcp_common_pdu_hdr, plen); 236bbd5b6feSJohn Baldwin return (EBADMSG); 237bbd5b6feSJohn Baldwin } 238bbd5b6feSJohn Baldwin 239bbd5b6feSJohn Baldwin switch (ch->pdu_type) { 240bbd5b6feSJohn Baldwin default: 241bbd5b6feSJohn Baldwin __assert_unreachable(); 242bbd5b6feSJohn Baldwin break; 243bbd5b6feSJohn Baldwin case NVME_TCP_PDU_TYPE_H2C_TERM_REQ: 244bbd5b6feSJohn Baldwin case NVME_TCP_PDU_TYPE_C2H_TERM_REQ: 245bbd5b6feSJohn Baldwin /* Checked above. */ 246bbd5b6feSJohn Baldwin MPASS(plen <= NVME_TCP_TERM_REQ_PDU_MAX_SIZE); 247bbd5b6feSJohn Baldwin break; 248bbd5b6feSJohn Baldwin case NVME_TCP_PDU_TYPE_CAPSULE_CMD: 249bbd5b6feSJohn Baldwin case NVME_TCP_PDU_TYPE_H2C_DATA: 250bbd5b6feSJohn Baldwin case NVME_TCP_PDU_TYPE_C2H_DATA: 251bbd5b6feSJohn Baldwin if ((ch->flags & NVME_TCP_CH_FLAGS_DDGSTF) != 0 && 252bbd5b6feSJohn Baldwin data_len <= sizeof(uint32_t)) { 253bbd5b6feSJohn Baldwin printf("NVMe/TCP: PDU %u too short for digest\n", 254bbd5b6feSJohn Baldwin ch->pdu_type); 255bbd5b6feSJohn Baldwin *fes = NVME_TCP_TERM_REQ_FES_INVALID_HEADER_FIELD; 256bbd5b6feSJohn Baldwin *fei = offsetof(struct nvme_tcp_common_pdu_hdr, plen); 257bbd5b6feSJohn Baldwin return (EBADMSG); 258bbd5b6feSJohn Baldwin } 259bbd5b6feSJohn Baldwin break; 260bbd5b6feSJohn Baldwin case NVME_TCP_PDU_TYPE_R2T: 261bbd5b6feSJohn Baldwin case NVME_TCP_PDU_TYPE_CAPSULE_RESP: 262bbd5b6feSJohn Baldwin if (data_len != 0) { 263bbd5b6feSJohn Baldwin printf("NVMe/TCP: PDU %u with data length %u\n", 264bbd5b6feSJohn Baldwin ch->pdu_type, data_len); 265bbd5b6feSJohn Baldwin *fes = NVME_TCP_TERM_REQ_FES_INVALID_HEADER_FIELD; 266bbd5b6feSJohn Baldwin *fei = offsetof(struct nvme_tcp_common_pdu_hdr, plen); 267bbd5b6feSJohn Baldwin return (EBADMSG); 268bbd5b6feSJohn Baldwin } 269bbd5b6feSJohn Baldwin break; 270bbd5b6feSJohn Baldwin } 271bbd5b6feSJohn Baldwin 272bbd5b6feSJohn Baldwin if ((ch->flags & NVME_TCP_CH_FLAGS_DDGSTF) != 0) 273bbd5b6feSJohn Baldwin data_len -= sizeof(uint32_t); 274bbd5b6feSJohn Baldwin 275bbd5b6feSJohn Baldwin *data_lenp = data_len; 276bbd5b6feSJohn Baldwin return (0); 277bbd5b6feSJohn Baldwin } 278bbd5b6feSJohn Baldwin 279bbd5b6feSJohn Baldwin #endif /* !__NVMF_TCP_H__ */ 280