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