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
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)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 full_hlen = ch->hlen;
45 if ((ch->flags & NVME_TCP_CH_FLAGS_HDGSTF) != 0)
46 full_hlen += sizeof(uint32_t);
47 if (plen == full_hlen)
48 data_len = 0;
49 else
50 data_len = plen - ch->pdo;
51
52 /*
53 * Errors must be reported for the lowest incorrect field
54 * first, so validate fields in order.
55 */
56
57 /* Validate pdu_type. */
58
59 /* Controllers only receive PDUs with a PDU direction of 0. */
60 if (controller != ((ch->pdu_type & 0x01) == 0)) {
61 printf("NVMe/TCP: Invalid PDU type %u\n", ch->pdu_type);
62 *fes = NVME_TCP_TERM_REQ_FES_INVALID_HEADER_FIELD;
63 *fei = offsetof(struct nvme_tcp_common_pdu_hdr, pdu_type);
64 return (EBADMSG);
65 }
66
67 switch (ch->pdu_type) {
68 case NVME_TCP_PDU_TYPE_IC_REQ:
69 case NVME_TCP_PDU_TYPE_IC_RESP:
70 /* Shouldn't get these for an established connection. */
71 printf("NVMe/TCP: Received Initialize Connection PDU\n");
72 *fes = NVME_TCP_TERM_REQ_FES_INVALID_HEADER_FIELD;
73 *fei = offsetof(struct nvme_tcp_common_pdu_hdr, pdu_type);
74 return (EBADMSG);
75 case NVME_TCP_PDU_TYPE_H2C_TERM_REQ:
76 case NVME_TCP_PDU_TYPE_C2H_TERM_REQ:
77 /*
78 * 7.4.7 Termination requests with invalid PDU lengths
79 * result in an immediate connection termination
80 * without reporting an error.
81 */
82 if (plen < sizeof(struct nvme_tcp_term_req_hdr) ||
83 plen > NVME_TCP_TERM_REQ_PDU_MAX_SIZE) {
84 printf("NVMe/TCP: Received invalid termination request\n");
85 return (ECONNRESET);
86 }
87 break;
88 case NVME_TCP_PDU_TYPE_CAPSULE_CMD:
89 case NVME_TCP_PDU_TYPE_CAPSULE_RESP:
90 case NVME_TCP_PDU_TYPE_H2C_DATA:
91 case NVME_TCP_PDU_TYPE_C2H_DATA:
92 case NVME_TCP_PDU_TYPE_R2T:
93 break;
94 default:
95 printf("NVMe/TCP: Invalid PDU type %u\n", ch->pdu_type);
96 *fes = NVME_TCP_TERM_REQ_FES_INVALID_HEADER_FIELD;
97 *fei = offsetof(struct nvme_tcp_common_pdu_hdr, pdu_type);
98 return (EBADMSG);
99 }
100
101 /* Validate flags. */
102 switch (ch->pdu_type) {
103 default:
104 __assert_unreachable();
105 break;
106 case NVME_TCP_PDU_TYPE_H2C_TERM_REQ:
107 case NVME_TCP_PDU_TYPE_C2H_TERM_REQ:
108 valid_flags = 0;
109 break;
110 case NVME_TCP_PDU_TYPE_CAPSULE_CMD:
111 valid_flags = NVME_TCP_CH_FLAGS_HDGSTF |
112 NVME_TCP_CH_FLAGS_DDGSTF;
113 break;
114 case NVME_TCP_PDU_TYPE_CAPSULE_RESP:
115 case NVME_TCP_PDU_TYPE_R2T:
116 valid_flags = NVME_TCP_CH_FLAGS_HDGSTF;
117 break;
118 case NVME_TCP_PDU_TYPE_H2C_DATA:
119 valid_flags = NVME_TCP_CH_FLAGS_HDGSTF |
120 NVME_TCP_CH_FLAGS_DDGSTF | NVME_TCP_H2C_DATA_FLAGS_LAST_PDU;
121 break;
122 case NVME_TCP_PDU_TYPE_C2H_DATA:
123 valid_flags = NVME_TCP_CH_FLAGS_HDGSTF |
124 NVME_TCP_CH_FLAGS_DDGSTF | NVME_TCP_C2H_DATA_FLAGS_LAST_PDU |
125 NVME_TCP_C2H_DATA_FLAGS_SUCCESS;
126 break;
127 }
128 if ((ch->flags & ~valid_flags) != 0) {
129 printf("NVMe/TCP: Invalid PDU header flags %#x\n", ch->flags);
130 *fes = NVME_TCP_TERM_REQ_FES_INVALID_HEADER_FIELD;
131 *fei = offsetof(struct nvme_tcp_common_pdu_hdr, flags);
132 return (EBADMSG);
133 }
134
135 /*
136 * Verify that digests are present iff enabled. Note that the
137 * data digest will not be present if there is no data
138 * payload.
139 */
140 digest_flags = 0;
141 if (header_digests)
142 digest_flags |= NVME_TCP_CH_FLAGS_HDGSTF;
143 if (data_digests && data_len != 0)
144 digest_flags |= NVME_TCP_CH_FLAGS_DDGSTF;
145 if ((digest_flags & valid_flags) !=
146 (ch->flags & (NVME_TCP_CH_FLAGS_HDGSTF |
147 NVME_TCP_CH_FLAGS_DDGSTF))) {
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 /* 7.4.5.2: SUCCESS in C2H requires LAST_PDU */
155 if (ch->pdu_type == NVME_TCP_PDU_TYPE_C2H_DATA &&
156 (ch->flags & (NVME_TCP_C2H_DATA_FLAGS_LAST_PDU |
157 NVME_TCP_C2H_DATA_FLAGS_SUCCESS)) ==
158 NVME_TCP_C2H_DATA_FLAGS_SUCCESS) {
159 printf("NVMe/TCP: Invalid PDU header flags %#x\n", ch->flags);
160 *fes = NVME_TCP_TERM_REQ_FES_INVALID_HEADER_FIELD;
161 *fei = offsetof(struct nvme_tcp_common_pdu_hdr, flags);
162 return (EBADMSG);
163 }
164
165 /* Validate hlen. */
166 switch (ch->pdu_type) {
167 default:
168 __assert_unreachable();
169 break;
170 case NVME_TCP_PDU_TYPE_H2C_TERM_REQ:
171 case NVME_TCP_PDU_TYPE_C2H_TERM_REQ:
172 expected_hlen = sizeof(struct nvme_tcp_term_req_hdr);
173 break;
174 case NVME_TCP_PDU_TYPE_CAPSULE_CMD:
175 expected_hlen = sizeof(struct nvme_tcp_cmd);
176 break;
177 case NVME_TCP_PDU_TYPE_CAPSULE_RESP:
178 expected_hlen = sizeof(struct nvme_tcp_rsp);
179 break;
180 case NVME_TCP_PDU_TYPE_H2C_DATA:
181 expected_hlen = sizeof(struct nvme_tcp_h2c_data_hdr);
182 break;
183 case NVME_TCP_PDU_TYPE_C2H_DATA:
184 expected_hlen = sizeof(struct nvme_tcp_c2h_data_hdr);
185 break;
186 case NVME_TCP_PDU_TYPE_R2T:
187 expected_hlen = sizeof(struct nvme_tcp_r2t_hdr);
188 break;
189 }
190 if (ch->hlen != expected_hlen) {
191 printf("NVMe/TCP: Invalid PDU header length %u\n", ch->hlen);
192 *fes = NVME_TCP_TERM_REQ_FES_INVALID_HEADER_FIELD;
193 *fei = offsetof(struct nvme_tcp_common_pdu_hdr, hlen);
194 return (EBADMSG);
195 }
196
197 /* Validate pdo. */
198 switch (ch->pdu_type) {
199 default:
200 __assert_unreachable();
201 break;
202 case NVME_TCP_PDU_TYPE_H2C_TERM_REQ:
203 case NVME_TCP_PDU_TYPE_C2H_TERM_REQ:
204 case NVME_TCP_PDU_TYPE_CAPSULE_RESP:
205 case NVME_TCP_PDU_TYPE_R2T:
206 if (ch->pdo != 0) {
207 printf("NVMe/TCP: Invalid PDU data offset %u\n",
208 ch->pdo);
209 *fes = NVME_TCP_TERM_REQ_FES_INVALID_HEADER_FIELD;
210 *fei = offsetof(struct nvme_tcp_common_pdu_hdr, pdo);
211 return (EBADMSG);
212 }
213 break;
214 case NVME_TCP_PDU_TYPE_CAPSULE_CMD:
215 case NVME_TCP_PDU_TYPE_H2C_DATA:
216 case NVME_TCP_PDU_TYPE_C2H_DATA:
217 /* Permit PDO of 0 if there is no data. */
218 if (data_len == 0 && ch->pdo == 0)
219 break;
220
221 if (ch->pdo < full_hlen || ch->pdo > plen ||
222 ch->pdo % rxpda != 0) {
223 printf("NVMe/TCP: Invalid PDU data offset %u\n",
224 ch->pdo);
225 *fes = NVME_TCP_TERM_REQ_FES_INVALID_HEADER_FIELD;
226 *fei = offsetof(struct nvme_tcp_common_pdu_hdr, pdo);
227 return (EBADMSG);
228 }
229 break;
230 }
231
232 /* Validate plen. */
233 if (plen < ch->hlen) {
234 printf("NVMe/TCP: Invalid PDU length %u\n", plen);
235 *fes = NVME_TCP_TERM_REQ_FES_INVALID_HEADER_FIELD;
236 *fei = offsetof(struct nvme_tcp_common_pdu_hdr, plen);
237 return (EBADMSG);
238 }
239
240 switch (ch->pdu_type) {
241 default:
242 __assert_unreachable();
243 break;
244 case NVME_TCP_PDU_TYPE_H2C_TERM_REQ:
245 case NVME_TCP_PDU_TYPE_C2H_TERM_REQ:
246 /* Checked above. */
247 MPASS(plen <= NVME_TCP_TERM_REQ_PDU_MAX_SIZE);
248 break;
249 case NVME_TCP_PDU_TYPE_CAPSULE_CMD:
250 case NVME_TCP_PDU_TYPE_H2C_DATA:
251 case NVME_TCP_PDU_TYPE_C2H_DATA:
252 if ((ch->flags & NVME_TCP_CH_FLAGS_DDGSTF) != 0 &&
253 data_len <= sizeof(uint32_t)) {
254 printf("NVMe/TCP: PDU %u too short for digest\n",
255 ch->pdu_type);
256 *fes = NVME_TCP_TERM_REQ_FES_INVALID_HEADER_FIELD;
257 *fei = offsetof(struct nvme_tcp_common_pdu_hdr, plen);
258 return (EBADMSG);
259 }
260 break;
261 case NVME_TCP_PDU_TYPE_R2T:
262 case NVME_TCP_PDU_TYPE_CAPSULE_RESP:
263 if (data_len != 0) {
264 printf("NVMe/TCP: PDU %u with data length %u\n",
265 ch->pdu_type, data_len);
266 *fes = NVME_TCP_TERM_REQ_FES_INVALID_HEADER_FIELD;
267 *fei = offsetof(struct nvme_tcp_common_pdu_hdr, plen);
268 return (EBADMSG);
269 }
270 break;
271 }
272
273 if ((ch->flags & NVME_TCP_CH_FLAGS_DDGSTF) != 0)
274 data_len -= sizeof(uint32_t);
275
276 *data_lenp = data_len;
277 return (0);
278 }
279
280 #endif /* !__NVMF_TCP_H__ */
281