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 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 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 * 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 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 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 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 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 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 165 nvmf_send_success(const struct nvmf_capsule *nc) 166 { 167 return (nvmf_send_generic_error(nc, NVME_SC_SUCCESS)); 168 } 169 170 void 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 * 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 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 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 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 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 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 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 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 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 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