1 /*-
2 * SPDX-License-Identifier: BSD-2-Clause
3 *
4 * Copyright (c) 2024 Chelsio Communications, Inc.
5 * Written by: John Baldwin <jhb@FreeBSD.org>
6 */
7
8 #include <sys/utsname.h>
9 #include <assert.h>
10 #include <ctype.h>
11 #include <errno.h>
12 #include <string.h>
13 #include <unistd.h>
14
15 #include "libnvmf.h"
16 #include "internal.h"
17 #include "nvmft_subr.h"
18
19 bool
nvmf_nqn_valid_strict(const char * nqn)20 nvmf_nqn_valid_strict(const char *nqn)
21 {
22 size_t len;
23
24 if (!nvmf_nqn_valid(nqn))
25 return (false);
26
27 /*
28 * Stricter checks from the spec. Linux does not seem to
29 * require these.
30 */
31 len = strlen(nqn);
32
33 /*
34 * NVMF_NQN_MIN_LEN does not include '.' and require at least
35 * one character of a domain name.
36 */
37 if (len < NVMF_NQN_MIN_LEN + 2)
38 return (false);
39 if (memcmp("nqn.", nqn, strlen("nqn.")) != 0)
40 return (false);
41 nqn += strlen("nqn.");
42
43 /* Next 4 digits must be a year. */
44 for (u_int i = 0; i < 4; i++) {
45 if (!isdigit(nqn[i]))
46 return (false);
47 }
48 nqn += 4;
49
50 /* '-' between year and month. */
51 if (nqn[0] != '-')
52 return (false);
53 nqn++;
54
55 /* 2 digit month. */
56 for (u_int i = 0; i < 2; i++) {
57 if (!isdigit(nqn[i]))
58 return (false);
59 }
60 nqn += 2;
61
62 /* '.' between month and reverse domain name. */
63 if (nqn[0] != '.')
64 return (false);
65 return (true);
66 }
67
68 void
nvmf_init_cqe(void * cqe,const struct nvmf_capsule * nc,uint16_t status)69 nvmf_init_cqe(void *cqe, const struct nvmf_capsule *nc, uint16_t status)
70 {
71 struct nvme_completion *cpl = cqe;
72 const struct nvme_command *cmd = nvmf_capsule_sqe(nc);
73
74 memset(cpl, 0, sizeof(*cpl));
75 cpl->cid = cmd->cid;
76 cpl->status = htole16(status);
77 }
78
79 static struct nvmf_capsule *
nvmf_simple_response(const struct nvmf_capsule * nc,uint8_t sc_type,uint8_t sc_status)80 nvmf_simple_response(const struct nvmf_capsule *nc, uint8_t sc_type,
81 uint8_t sc_status)
82 {
83 struct nvme_completion cpl;
84 uint16_t status;
85
86 status = NVMEF(NVME_STATUS_SCT, sc_type) |
87 NVMEF(NVME_STATUS_SC, sc_status);
88 nvmf_init_cqe(&cpl, nc, status);
89 return (nvmf_allocate_response(nc->nc_qpair, &cpl));
90 }
91
92 int
nvmf_controller_receive_capsule(struct nvmf_qpair * qp,struct nvmf_capsule ** ncp)93 nvmf_controller_receive_capsule(struct nvmf_qpair *qp,
94 struct nvmf_capsule **ncp)
95 {
96 struct nvmf_capsule *nc;
97 int error;
98 uint8_t sc_status;
99
100 *ncp = NULL;
101 error = nvmf_receive_capsule(qp, &nc);
102 if (error != 0)
103 return (error);
104
105 sc_status = nvmf_validate_command_capsule(nc);
106 if (sc_status != NVME_SC_SUCCESS) {
107 nvmf_send_generic_error(nc, sc_status);
108 nvmf_free_capsule(nc);
109 return (EPROTO);
110 }
111
112 *ncp = nc;
113 return (0);
114 }
115
116 int
nvmf_controller_transmit_response(struct nvmf_capsule * nc)117 nvmf_controller_transmit_response(struct nvmf_capsule *nc)
118 {
119 struct nvmf_qpair *qp = nc->nc_qpair;
120
121 /* Set SQHD. */
122 if (qp->nq_flow_control) {
123 qp->nq_sqhd = (qp->nq_sqhd + 1) % qp->nq_qsize;
124 nc->nc_cqe.sqhd = htole16(qp->nq_sqhd);
125 } else
126 nc->nc_cqe.sqhd = 0;
127
128 return (nvmf_transmit_capsule(nc));
129 }
130
131 int
nvmf_send_response(const struct nvmf_capsule * cc,const void * cqe)132 nvmf_send_response(const struct nvmf_capsule *cc, const void *cqe)
133 {
134 struct nvmf_capsule *rc;
135 int error;
136
137 rc = nvmf_allocate_response(cc->nc_qpair, cqe);
138 if (rc == NULL)
139 return (ENOMEM);
140 error = nvmf_controller_transmit_response(rc);
141 nvmf_free_capsule(rc);
142 return (error);
143 }
144
145 int
nvmf_send_error(const struct nvmf_capsule * cc,uint8_t sc_type,uint8_t sc_status)146 nvmf_send_error(const struct nvmf_capsule *cc, uint8_t sc_type,
147 uint8_t sc_status)
148 {
149 struct nvmf_capsule *rc;
150 int error;
151
152 rc = nvmf_simple_response(cc, sc_type, sc_status);
153 error = nvmf_controller_transmit_response(rc);
154 nvmf_free_capsule(rc);
155 return (error);
156 }
157
158 int
nvmf_send_generic_error(const struct nvmf_capsule * nc,uint8_t sc_status)159 nvmf_send_generic_error(const struct nvmf_capsule *nc, uint8_t sc_status)
160 {
161 return (nvmf_send_error(nc, NVME_SCT_GENERIC, sc_status));
162 }
163
164 int
nvmf_send_success(const struct nvmf_capsule * nc)165 nvmf_send_success(const struct nvmf_capsule *nc)
166 {
167 return (nvmf_send_generic_error(nc, NVME_SC_SUCCESS));
168 }
169
170 void
nvmf_connect_invalid_parameters(const struct nvmf_capsule * cc,bool data,uint16_t offset)171 nvmf_connect_invalid_parameters(const struct nvmf_capsule *cc, bool data,
172 uint16_t offset)
173 {
174 struct nvmf_fabric_connect_rsp rsp;
175 struct nvmf_capsule *rc;
176
177 nvmf_init_cqe(&rsp, cc,
178 NVMEF(NVME_STATUS_SCT, NVME_SCT_COMMAND_SPECIFIC) |
179 NVMEF(NVME_STATUS_SC, NVMF_FABRIC_SC_INVALID_PARAM));
180 rsp.status_code_specific.invalid.ipo = htole16(offset);
181 rsp.status_code_specific.invalid.iattr = data ? 1 : 0;
182 rc = nvmf_allocate_response(cc->nc_qpair, &rsp);
183 nvmf_transmit_capsule(rc);
184 nvmf_free_capsule(rc);
185 }
186
187 struct nvmf_qpair *
nvmf_accept(struct nvmf_association * na,const struct nvmf_qpair_params * params,struct nvmf_capsule ** ccp,struct nvmf_fabric_connect_data * data)188 nvmf_accept(struct nvmf_association *na, const struct nvmf_qpair_params *params,
189 struct nvmf_capsule **ccp, struct nvmf_fabric_connect_data *data)
190 {
191 static const char hostid_zero[sizeof(data->hostid)];
192 const struct nvmf_fabric_connect_cmd *cmd;
193 struct nvmf_qpair *qp;
194 struct nvmf_capsule *cc, *rc;
195 u_int qsize;
196 int error;
197 uint16_t cntlid;
198 uint8_t sc_status;
199
200 qp = NULL;
201 cc = NULL;
202 rc = NULL;
203 *ccp = NULL;
204 na_clear_error(na);
205 if (!na->na_controller) {
206 na_error(na, "Cannot accept on a host");
207 goto error;
208 }
209
210 qp = nvmf_allocate_qpair(na, params);
211 if (qp == NULL)
212 goto error;
213
214 /* Read the CONNECT capsule. */
215 error = nvmf_receive_capsule(qp, &cc);
216 if (error != 0) {
217 na_error(na, "Failed to receive CONNECT: %s", strerror(error));
218 goto error;
219 }
220
221 sc_status = nvmf_validate_command_capsule(cc);
222 if (sc_status != 0) {
223 na_error(na, "CONNECT command failed to validate: %u",
224 sc_status);
225 rc = nvmf_simple_response(cc, NVME_SCT_GENERIC, sc_status);
226 goto error;
227 }
228
229 cmd = nvmf_capsule_sqe(cc);
230 if (cmd->opcode != NVME_OPC_FABRICS_COMMANDS ||
231 cmd->fctype != NVMF_FABRIC_COMMAND_CONNECT) {
232 na_error(na, "Invalid opcode in CONNECT (%u,%u)", cmd->opcode,
233 cmd->fctype);
234 rc = nvmf_simple_response(cc, NVME_SCT_GENERIC,
235 NVME_SC_INVALID_OPCODE);
236 goto error;
237 }
238
239 if (cmd->recfmt != htole16(0)) {
240 na_error(na, "Unsupported CONNECT record format %u",
241 le16toh(cmd->recfmt));
242 rc = nvmf_simple_response(cc, NVME_SCT_COMMAND_SPECIFIC,
243 NVMF_FABRIC_SC_INCOMPATIBLE_FORMAT);
244 goto error;
245 }
246
247 qsize = le16toh(cmd->sqsize) + 1;
248 if (cmd->qid == 0) {
249 /* Admin queue limits. */
250 if (qsize < NVME_MIN_ADMIN_ENTRIES ||
251 qsize > NVME_MAX_ADMIN_ENTRIES ||
252 qsize > na->na_params.max_admin_qsize) {
253 na_error(na, "Invalid queue size %u", qsize);
254 nvmf_connect_invalid_parameters(cc, false,
255 offsetof(struct nvmf_fabric_connect_cmd, sqsize));
256 goto error;
257 }
258 qp->nq_admin = true;
259 } else {
260 /* I/O queues not allowed for discovery. */
261 if (na->na_params.max_io_qsize == 0) {
262 na_error(na, "I/O queue on discovery controller");
263 nvmf_connect_invalid_parameters(cc, false,
264 offsetof(struct nvmf_fabric_connect_cmd, qid));
265 goto error;
266 }
267
268 /* I/O queue limits. */
269 if (qsize < NVME_MIN_IO_ENTRIES ||
270 qsize > NVME_MAX_IO_ENTRIES ||
271 qsize > na->na_params.max_io_qsize) {
272 na_error(na, "Invalid queue size %u", qsize);
273 nvmf_connect_invalid_parameters(cc, false,
274 offsetof(struct nvmf_fabric_connect_cmd, sqsize));
275 goto error;
276 }
277
278 /* KATO is reserved for I/O queues. */
279 if (cmd->kato != 0) {
280 na_error(na,
281 "KeepAlive timeout specified for I/O queue");
282 nvmf_connect_invalid_parameters(cc, false,
283 offsetof(struct nvmf_fabric_connect_cmd, kato));
284 goto error;
285 }
286 qp->nq_admin = false;
287 }
288 qp->nq_qsize = qsize;
289
290 /* Fetch CONNECT data. */
291 if (nvmf_capsule_data_len(cc) != sizeof(*data)) {
292 na_error(na, "Invalid data payload length for CONNECT: %zu",
293 nvmf_capsule_data_len(cc));
294 nvmf_connect_invalid_parameters(cc, false,
295 offsetof(struct nvmf_fabric_connect_cmd, sgl1));
296 goto error;
297 }
298
299 error = nvmf_receive_controller_data(cc, 0, data, sizeof(*data));
300 if (error != 0) {
301 na_error(na, "Failed to read data for CONNECT: %s",
302 strerror(error));
303 rc = nvmf_simple_response(cc, NVME_SCT_GENERIC,
304 NVME_SC_DATA_TRANSFER_ERROR);
305 goto error;
306 }
307
308 /* The hostid must be non-zero. */
309 if (memcmp(data->hostid, hostid_zero, sizeof(hostid_zero)) == 0) {
310 na_error(na, "HostID in CONNECT data is zero");
311 nvmf_connect_invalid_parameters(cc, true,
312 offsetof(struct nvmf_fabric_connect_data, hostid));
313 goto error;
314 }
315
316 cntlid = le16toh(data->cntlid);
317 if (cmd->qid == 0) {
318 if (na->na_params.dynamic_controller_model) {
319 if (cntlid != NVMF_CNTLID_DYNAMIC) {
320 na_error(na, "Invalid controller ID %#x",
321 cntlid);
322 nvmf_connect_invalid_parameters(cc, true,
323 offsetof(struct nvmf_fabric_connect_data,
324 cntlid));
325 goto error;
326 }
327 } else {
328 if (cntlid > NVMF_CNTLID_STATIC_MAX &&
329 cntlid != NVMF_CNTLID_STATIC_ANY) {
330 na_error(na, "Invalid controller ID %#x",
331 cntlid);
332 nvmf_connect_invalid_parameters(cc, true,
333 offsetof(struct nvmf_fabric_connect_data,
334 cntlid));
335 goto error;
336 }
337 }
338 } else {
339 /* Wildcard Controller IDs are only valid on an Admin queue. */
340 if (cntlid > NVMF_CNTLID_STATIC_MAX) {
341 na_error(na, "Invalid controller ID %#x", cntlid);
342 nvmf_connect_invalid_parameters(cc, true,
343 offsetof(struct nvmf_fabric_connect_data, cntlid));
344 goto error;
345 }
346 }
347
348 /* Simple validation of each NQN. */
349 if (!nvmf_nqn_valid(data->subnqn)) {
350 na_error(na, "Invalid SubNQN %.*s", (int)sizeof(data->subnqn),
351 data->subnqn);
352 nvmf_connect_invalid_parameters(cc, true,
353 offsetof(struct nvmf_fabric_connect_data, subnqn));
354 goto error;
355 }
356 if (!nvmf_nqn_valid(data->hostnqn)) {
357 na_error(na, "Invalid HostNQN %.*s", (int)sizeof(data->hostnqn),
358 data->hostnqn);
359 nvmf_connect_invalid_parameters(cc, true,
360 offsetof(struct nvmf_fabric_connect_data, hostnqn));
361 goto error;
362 }
363
364 if (na->na_params.sq_flow_control ||
365 (cmd->cattr & NVMF_CONNECT_ATTR_DISABLE_SQ_FC) == 0)
366 qp->nq_flow_control = true;
367 else
368 qp->nq_flow_control = false;
369 qp->nq_sqhd = 0;
370 qp->nq_kato = le32toh(cmd->kato);
371 *ccp = cc;
372 return (qp);
373 error:
374 if (rc != NULL) {
375 nvmf_transmit_capsule(rc);
376 nvmf_free_capsule(rc);
377 }
378 if (cc != NULL)
379 nvmf_free_capsule(cc);
380 if (qp != NULL)
381 nvmf_free_qpair(qp);
382 return (NULL);
383 }
384
385 int
nvmf_finish_accept(const struct nvmf_capsule * cc,uint16_t cntlid)386 nvmf_finish_accept(const struct nvmf_capsule *cc, uint16_t cntlid)
387 {
388 struct nvmf_fabric_connect_rsp rsp;
389 struct nvmf_qpair *qp = cc->nc_qpair;
390 struct nvmf_capsule *rc;
391 int error;
392
393 nvmf_init_cqe(&rsp, cc, 0);
394 if (qp->nq_flow_control)
395 rsp.sqhd = htole16(qp->nq_sqhd);
396 else
397 rsp.sqhd = htole16(0xffff);
398 rsp.status_code_specific.success.cntlid = htole16(cntlid);
399 rc = nvmf_allocate_response(qp, &rsp);
400 if (rc == NULL)
401 return (ENOMEM);
402 error = nvmf_transmit_capsule(rc);
403 nvmf_free_capsule(rc);
404 if (error == 0)
405 qp->nq_cntlid = cntlid;
406 return (error);
407 }
408
409 uint64_t
nvmf_controller_cap(struct nvmf_qpair * qp)410 nvmf_controller_cap(struct nvmf_qpair *qp)
411 {
412 const struct nvmf_association *na = qp->nq_association;
413
414 return (_nvmf_controller_cap(na->na_params.max_io_qsize,
415 NVMF_CC_EN_TIMEOUT));
416 }
417
418 bool
nvmf_validate_cc(struct nvmf_qpair * qp,uint64_t cap,uint32_t old_cc,uint32_t new_cc)419 nvmf_validate_cc(struct nvmf_qpair *qp, uint64_t cap, uint32_t old_cc,
420 uint32_t new_cc)
421 {
422 const struct nvmf_association *na = qp->nq_association;
423
424 return (_nvmf_validate_cc(na->na_params.max_io_qsize, cap, old_cc,
425 new_cc));
426 }
427
428 void
nvmf_init_discovery_controller_data(struct nvmf_qpair * qp,struct nvme_controller_data * cdata)429 nvmf_init_discovery_controller_data(struct nvmf_qpair *qp,
430 struct nvme_controller_data *cdata)
431 {
432 const struct nvmf_association *na = qp->nq_association;
433 struct utsname utsname;
434 char *cp;
435
436 memset(cdata, 0, sizeof(*cdata));
437
438 /*
439 * 5.2 Figure 37 states model name and serial are reserved,
440 * but Linux includes them. Don't bother with serial, but
441 * do set model name.
442 */
443 uname(&utsname);
444 nvmf_strpad(cdata->mn, utsname.sysname, sizeof(cdata->mn));
445 nvmf_strpad(cdata->fr, utsname.release, sizeof(cdata->fr));
446 cp = memchr(cdata->fr, '-', sizeof(cdata->fr));
447 if (cp != NULL)
448 memset(cp, ' ', sizeof(cdata->fr) - (cp - (char *)cdata->fr));
449
450 cdata->ctrlr_id = htole16(qp->nq_cntlid);
451 cdata->ver = htole32(NVME_REV(1, 4));
452 cdata->cntrltype = 2;
453
454 cdata->lpa = NVMEF(NVME_CTRLR_DATA_LPA_EXT_DATA, 1);
455 cdata->elpe = 0;
456
457 cdata->maxcmd = htole16(na->na_params.max_admin_qsize);
458
459 /* Transport-specific? */
460 cdata->sgls = htole32(
461 NVMEF(NVME_CTRLR_DATA_SGLS_TRANSPORT_DATA_BLOCK, 1) |
462 NVMEF(NVME_CTRLR_DATA_SGLS_ADDRESS_AS_OFFSET, 1) |
463 NVMEF(NVME_CTRLR_DATA_SGLS_NVM_COMMAND_SET, 1));
464
465 strlcpy(cdata->subnqn, NVMF_DISCOVERY_NQN, sizeof(cdata->subnqn));
466 }
467
468 void
nvmf_init_io_controller_data(struct nvmf_qpair * qp,const char * serial,const char * subnqn,int nn,uint32_t ioccsz,struct nvme_controller_data * cdata)469 nvmf_init_io_controller_data(struct nvmf_qpair *qp, const char *serial,
470 const char *subnqn, int nn, uint32_t ioccsz,
471 struct nvme_controller_data *cdata)
472 {
473 const struct nvmf_association *na = qp->nq_association;
474 struct utsname utsname;
475
476 uname(&utsname);
477
478 memset(cdata, 0, sizeof(*cdata));
479 _nvmf_init_io_controller_data(qp->nq_cntlid, na->na_params.max_io_qsize,
480 serial, utsname.sysname, utsname.release, subnqn, nn, ioccsz,
481 sizeof(struct nvme_completion), cdata);
482 }
483
484 uint8_t
nvmf_get_log_page_id(const struct nvme_command * cmd)485 nvmf_get_log_page_id(const struct nvme_command *cmd)
486 {
487 assert(cmd->opc == NVME_OPC_GET_LOG_PAGE);
488 return (le32toh(cmd->cdw10) & 0xff);
489 }
490
491 uint64_t
nvmf_get_log_page_length(const struct nvme_command * cmd)492 nvmf_get_log_page_length(const struct nvme_command *cmd)
493 {
494 uint32_t numd;
495
496 assert(cmd->opc == NVME_OPC_GET_LOG_PAGE);
497 numd = le32toh(cmd->cdw10) >> 16 | (le32toh(cmd->cdw11) & 0xffff) << 16;
498 return ((numd + 1) * 4);
499 }
500
501 uint64_t
nvmf_get_log_page_offset(const struct nvme_command * cmd)502 nvmf_get_log_page_offset(const struct nvme_command *cmd)
503 {
504 assert(cmd->opc == NVME_OPC_GET_LOG_PAGE);
505 return (le32toh(cmd->cdw12) | (uint64_t)le32toh(cmd->cdw13) << 32);
506 }
507
508 int
nvmf_handoff_controller_qpair(struct nvmf_qpair * qp,const struct nvmf_fabric_connect_cmd * cmd,const struct nvmf_fabric_connect_data * data,struct nvmf_ioc_nv * nv)509 nvmf_handoff_controller_qpair(struct nvmf_qpair *qp,
510 const struct nvmf_fabric_connect_cmd *cmd,
511 const struct nvmf_fabric_connect_data *data, struct nvmf_ioc_nv *nv)
512 {
513 nvlist_t *nvl, *nvl_qp;
514 int error;
515
516 error = nvmf_kernel_handoff_params(qp, &nvl_qp);
517 if (error)
518 return (error);
519
520 nvl = nvlist_create(0);
521 nvlist_add_number(nvl, "trtype", qp->nq_association->na_trtype);
522 nvlist_move_nvlist(nvl, "params", nvl_qp);
523 nvlist_add_binary(nvl, "cmd", cmd, sizeof(*cmd));
524 nvlist_add_binary(nvl, "data", data, sizeof(*data));
525
526 error = nvmf_pack_ioc_nvlist(nv, nvl);
527 nvlist_destroy(nvl);
528 return (error);
529 }
530