xref: /freebsd/sys/dev/nvmf/nvmf_tcp.h (revision 397e83df75e0fcd0d3fcb95ae4d794cb7600fc89)
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