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