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