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