1*66b5296fSJohn Baldwin /*- 2*66b5296fSJohn Baldwin * SPDX-License-Identifier: BSD-2-Clause 3*66b5296fSJohn Baldwin * 4*66b5296fSJohn Baldwin * Copyright (c) 2023-2025 Chelsio Communications, Inc. 5*66b5296fSJohn Baldwin * Written by: John Baldwin <jhb@FreeBSD.org> 6*66b5296fSJohn Baldwin */ 7*66b5296fSJohn Baldwin 8*66b5296fSJohn Baldwin #include <assert.h> 9*66b5296fSJohn Baldwin #include <errno.h> 10*66b5296fSJohn Baldwin #include <netdb.h> 11*66b5296fSJohn Baldwin #include <libiscsiutil.h> 12*66b5296fSJohn Baldwin #include <libnvmf.h> 13*66b5296fSJohn Baldwin #include <stdlib.h> 14*66b5296fSJohn Baldwin #include <string.h> 15*66b5296fSJohn Baldwin #include <unistd.h> 16*66b5296fSJohn Baldwin #include <netinet/in.h> 17*66b5296fSJohn Baldwin 18*66b5296fSJohn Baldwin #include "ctld.hh" 19*66b5296fSJohn Baldwin #include "nvmf.hh" 20*66b5296fSJohn Baldwin 21*66b5296fSJohn Baldwin struct discovery_log { 22*66b5296fSJohn Baldwin discovery_log(const struct portal_group *pg); 23*66b5296fSJohn Baldwin 24*66b5296fSJohn Baldwin const char *data() const { return buf.data(); } 25*66b5296fSJohn Baldwin size_t length() const { return buf.size(); } 26*66b5296fSJohn Baldwin 27*66b5296fSJohn Baldwin void append(const struct nvme_discovery_log_entry *entry); 28*66b5296fSJohn Baldwin 29*66b5296fSJohn Baldwin private: 30*66b5296fSJohn Baldwin struct nvme_discovery_log *header() 31*66b5296fSJohn Baldwin { return reinterpret_cast<struct nvme_discovery_log *>(buf.data()); } 32*66b5296fSJohn Baldwin 33*66b5296fSJohn Baldwin std::vector<char> buf; 34*66b5296fSJohn Baldwin }; 35*66b5296fSJohn Baldwin 36*66b5296fSJohn Baldwin struct discovery_controller { 37*66b5296fSJohn Baldwin discovery_controller(freebsd::fd_up s, struct nvmf_qpair *qp, 38*66b5296fSJohn Baldwin const discovery_log &discovery_log); 39*66b5296fSJohn Baldwin 40*66b5296fSJohn Baldwin void handle_admin_commands(); 41*66b5296fSJohn Baldwin private: 42*66b5296fSJohn Baldwin bool update_cc(uint32_t new_cc); 43*66b5296fSJohn Baldwin void handle_property_get(const struct nvmf_capsule *nc, 44*66b5296fSJohn Baldwin const struct nvmf_fabric_prop_get_cmd *pget); 45*66b5296fSJohn Baldwin void handle_property_set(const struct nvmf_capsule *nc, 46*66b5296fSJohn Baldwin const struct nvmf_fabric_prop_set_cmd *pset); 47*66b5296fSJohn Baldwin void handle_fabrics_command(const struct nvmf_capsule *nc, 48*66b5296fSJohn Baldwin const struct nvmf_fabric_cmd *cmd); 49*66b5296fSJohn Baldwin void handle_identify_command(const struct nvmf_capsule *nc, 50*66b5296fSJohn Baldwin const struct nvme_command *cmd); 51*66b5296fSJohn Baldwin void handle_get_log_page_command(const struct nvmf_capsule *nc, 52*66b5296fSJohn Baldwin const struct nvme_command *cmd); 53*66b5296fSJohn Baldwin 54*66b5296fSJohn Baldwin struct nvmf_qpair *qp; 55*66b5296fSJohn Baldwin 56*66b5296fSJohn Baldwin uint64_t cap = 0; 57*66b5296fSJohn Baldwin uint32_t vs = 0; 58*66b5296fSJohn Baldwin uint32_t cc = 0; 59*66b5296fSJohn Baldwin uint32_t csts = 0; 60*66b5296fSJohn Baldwin 61*66b5296fSJohn Baldwin bool shutdown = false; 62*66b5296fSJohn Baldwin 63*66b5296fSJohn Baldwin struct nvme_controller_data cdata; 64*66b5296fSJohn Baldwin 65*66b5296fSJohn Baldwin const discovery_log &discovery_log; 66*66b5296fSJohn Baldwin freebsd::fd_up s; 67*66b5296fSJohn Baldwin }; 68*66b5296fSJohn Baldwin 69*66b5296fSJohn Baldwin discovery_log::discovery_log(const struct portal_group *pg) : 70*66b5296fSJohn Baldwin buf(sizeof(nvme_discovery_log)) 71*66b5296fSJohn Baldwin { 72*66b5296fSJohn Baldwin struct nvme_discovery_log *log = header(); 73*66b5296fSJohn Baldwin 74*66b5296fSJohn Baldwin log->genctr = htole32(pg->conf()->genctr()); 75*66b5296fSJohn Baldwin log->recfmt = 0; 76*66b5296fSJohn Baldwin } 77*66b5296fSJohn Baldwin 78*66b5296fSJohn Baldwin void 79*66b5296fSJohn Baldwin discovery_log::append(const struct nvme_discovery_log_entry *entry) 80*66b5296fSJohn Baldwin { 81*66b5296fSJohn Baldwin const char *cp = reinterpret_cast<const char *>(entry); 82*66b5296fSJohn Baldwin buf.insert(buf.end(), cp, cp + sizeof(*entry)); 83*66b5296fSJohn Baldwin 84*66b5296fSJohn Baldwin struct nvme_discovery_log *log = header(); 85*66b5296fSJohn Baldwin log->numrec = htole32(le32toh(log->numrec) + 1); 86*66b5296fSJohn Baldwin } 87*66b5296fSJohn Baldwin 88*66b5296fSJohn Baldwin static bool 89*66b5296fSJohn Baldwin discovery_controller_filtered(const struct portal_group *pg, 90*66b5296fSJohn Baldwin const struct sockaddr *client_sa, std::string_view hostnqn, 91*66b5296fSJohn Baldwin const struct port *port) 92*66b5296fSJohn Baldwin { 93*66b5296fSJohn Baldwin const struct target *targ = port->target(); 94*66b5296fSJohn Baldwin const struct auth_group *ag = port->auth_group(); 95*66b5296fSJohn Baldwin if (ag == nullptr) 96*66b5296fSJohn Baldwin ag = targ->auth_group(); 97*66b5296fSJohn Baldwin 98*66b5296fSJohn Baldwin assert(pg->discovery_filter() != discovery_filter::UNKNOWN); 99*66b5296fSJohn Baldwin 100*66b5296fSJohn Baldwin if (pg->discovery_filter() >= discovery_filter::PORTAL && 101*66b5296fSJohn Baldwin !ag->host_permitted(client_sa)) { 102*66b5296fSJohn Baldwin log_debugx("host address does not match addresses " 103*66b5296fSJohn Baldwin "allowed for controller \"%s\"; skipping", targ->name()); 104*66b5296fSJohn Baldwin return true; 105*66b5296fSJohn Baldwin } 106*66b5296fSJohn Baldwin 107*66b5296fSJohn Baldwin if (pg->discovery_filter() >= discovery_filter::PORTAL_NAME && 108*66b5296fSJohn Baldwin !ag->host_permitted(hostnqn) != 0) { 109*66b5296fSJohn Baldwin log_debugx("HostNQN does not match NQNs " 110*66b5296fSJohn Baldwin "allowed for controller \"%s\"; skipping", targ->name()); 111*66b5296fSJohn Baldwin return true; 112*66b5296fSJohn Baldwin } 113*66b5296fSJohn Baldwin 114*66b5296fSJohn Baldwin /* XXX: auth not yet implemented for NVMe */ 115*66b5296fSJohn Baldwin 116*66b5296fSJohn Baldwin return false; 117*66b5296fSJohn Baldwin } 118*66b5296fSJohn Baldwin 119*66b5296fSJohn Baldwin static bool 120*66b5296fSJohn Baldwin portal_uses_wildcard_address(const struct portal *p) 121*66b5296fSJohn Baldwin { 122*66b5296fSJohn Baldwin const struct addrinfo *ai = p->ai(); 123*66b5296fSJohn Baldwin 124*66b5296fSJohn Baldwin switch (ai->ai_family) { 125*66b5296fSJohn Baldwin case AF_INET: 126*66b5296fSJohn Baldwin { 127*66b5296fSJohn Baldwin const struct sockaddr_in *sin; 128*66b5296fSJohn Baldwin 129*66b5296fSJohn Baldwin sin = (const struct sockaddr_in *)ai->ai_addr; 130*66b5296fSJohn Baldwin return sin->sin_addr.s_addr == htonl(INADDR_ANY); 131*66b5296fSJohn Baldwin } 132*66b5296fSJohn Baldwin case AF_INET6: 133*66b5296fSJohn Baldwin { 134*66b5296fSJohn Baldwin const struct sockaddr_in6 *sin6; 135*66b5296fSJohn Baldwin 136*66b5296fSJohn Baldwin sin6 = (const struct sockaddr_in6 *)ai->ai_addr; 137*66b5296fSJohn Baldwin return memcmp(&sin6->sin6_addr, &in6addr_any, 138*66b5296fSJohn Baldwin sizeof(in6addr_any)) == 0; 139*66b5296fSJohn Baldwin } 140*66b5296fSJohn Baldwin default: 141*66b5296fSJohn Baldwin __assert_unreachable(); 142*66b5296fSJohn Baldwin } 143*66b5296fSJohn Baldwin } 144*66b5296fSJohn Baldwin 145*66b5296fSJohn Baldwin static bool 146*66b5296fSJohn Baldwin init_discovery_log_entry(struct nvme_discovery_log_entry *entry, 147*66b5296fSJohn Baldwin const struct target *target, const struct portal *portal, 148*66b5296fSJohn Baldwin const char *wildcard_host) 149*66b5296fSJohn Baldwin { 150*66b5296fSJohn Baldwin /* 151*66b5296fSJohn Baldwin * The TCP port for I/O controllers might not be fixed, so 152*66b5296fSJohn Baldwin * fetch the sockaddr of the socket to determine which port 153*66b5296fSJohn Baldwin * the kernel chose. 154*66b5296fSJohn Baldwin */ 155*66b5296fSJohn Baldwin struct sockaddr_storage ss; 156*66b5296fSJohn Baldwin socklen_t len = sizeof(ss); 157*66b5296fSJohn Baldwin if (getsockname(portal->socket(), (struct sockaddr *)&ss, &len) == -1) { 158*66b5296fSJohn Baldwin log_warn("Failed getsockname building discovery log entry"); 159*66b5296fSJohn Baldwin return false; 160*66b5296fSJohn Baldwin } 161*66b5296fSJohn Baldwin 162*66b5296fSJohn Baldwin const struct nvmf_association_params *aparams = 163*66b5296fSJohn Baldwin static_cast<const nvmf_portal *>(portal)->aparams(); 164*66b5296fSJohn Baldwin 165*66b5296fSJohn Baldwin memset(entry, 0, sizeof(*entry)); 166*66b5296fSJohn Baldwin entry->trtype = NVMF_TRTYPE_TCP; 167*66b5296fSJohn Baldwin int error = getnameinfo((struct sockaddr *)&ss, len, 168*66b5296fSJohn Baldwin (char *)entry->traddr, sizeof(entry->traddr), 169*66b5296fSJohn Baldwin (char *)entry->trsvcid, sizeof(entry->trsvcid), 170*66b5296fSJohn Baldwin NI_NUMERICHOST | NI_NUMERICSERV); 171*66b5296fSJohn Baldwin if (error != 0) { 172*66b5296fSJohn Baldwin log_warnx("Failed getnameinfo building discovery log entry: %s", 173*66b5296fSJohn Baldwin gai_strerror(error)); 174*66b5296fSJohn Baldwin return false; 175*66b5296fSJohn Baldwin } 176*66b5296fSJohn Baldwin 177*66b5296fSJohn Baldwin if (portal_uses_wildcard_address(portal)) 178*66b5296fSJohn Baldwin strncpy((char *)entry->traddr, wildcard_host, 179*66b5296fSJohn Baldwin sizeof(entry->traddr)); 180*66b5296fSJohn Baldwin switch (portal->ai()->ai_family) { 181*66b5296fSJohn Baldwin case AF_INET: 182*66b5296fSJohn Baldwin entry->adrfam = NVMF_ADRFAM_IPV4; 183*66b5296fSJohn Baldwin break; 184*66b5296fSJohn Baldwin case AF_INET6: 185*66b5296fSJohn Baldwin entry->adrfam = NVMF_ADRFAM_IPV6; 186*66b5296fSJohn Baldwin break; 187*66b5296fSJohn Baldwin default: 188*66b5296fSJohn Baldwin __assert_unreachable(); 189*66b5296fSJohn Baldwin } 190*66b5296fSJohn Baldwin entry->subtype = NVMF_SUBTYPE_NVME; 191*66b5296fSJohn Baldwin if (!aparams->sq_flow_control) 192*66b5296fSJohn Baldwin entry->treq |= (1 << 2); 193*66b5296fSJohn Baldwin entry->portid = htole16(portal->portal_group()->tag()); 194*66b5296fSJohn Baldwin entry->cntlid = htole16(NVMF_CNTLID_DYNAMIC); 195*66b5296fSJohn Baldwin entry->aqsz = aparams->max_admin_qsize; 196*66b5296fSJohn Baldwin strncpy((char *)entry->subnqn, target->name(), sizeof(entry->subnqn)); 197*66b5296fSJohn Baldwin return true; 198*66b5296fSJohn Baldwin } 199*66b5296fSJohn Baldwin 200*66b5296fSJohn Baldwin static discovery_log 201*66b5296fSJohn Baldwin build_discovery_log_page(const struct portal_group *pg, int fd, 202*66b5296fSJohn Baldwin const struct sockaddr *client_sa, 203*66b5296fSJohn Baldwin const struct nvmf_fabric_connect_data &data) 204*66b5296fSJohn Baldwin { 205*66b5296fSJohn Baldwin discovery_log discovery_log(pg); 206*66b5296fSJohn Baldwin 207*66b5296fSJohn Baldwin struct sockaddr_storage ss; 208*66b5296fSJohn Baldwin socklen_t len = sizeof(ss); 209*66b5296fSJohn Baldwin if (getsockname(fd, (struct sockaddr *)&ss, &len) == -1) { 210*66b5296fSJohn Baldwin log_warn("build_discovery_log_page: getsockname"); 211*66b5296fSJohn Baldwin return discovery_log; 212*66b5296fSJohn Baldwin } 213*66b5296fSJohn Baldwin 214*66b5296fSJohn Baldwin char wildcard_host[NI_MAXHOST]; 215*66b5296fSJohn Baldwin int error = getnameinfo((struct sockaddr *)&ss, len, wildcard_host, 216*66b5296fSJohn Baldwin sizeof(wildcard_host), NULL, 0, NI_NUMERICHOST); 217*66b5296fSJohn Baldwin if (error != 0) { 218*66b5296fSJohn Baldwin log_warnx("build_discovery_log_page: getnameinfo: %s", 219*66b5296fSJohn Baldwin gai_strerror(error)); 220*66b5296fSJohn Baldwin return discovery_log; 221*66b5296fSJohn Baldwin } 222*66b5296fSJohn Baldwin 223*66b5296fSJohn Baldwin const char *nqn = (const char *)data.hostnqn; 224*66b5296fSJohn Baldwin std::string hostnqn(nqn, strnlen(nqn, sizeof(data.hostnqn))); 225*66b5296fSJohn Baldwin for (const auto &kv : pg->ports()) { 226*66b5296fSJohn Baldwin const struct port *port = kv.second; 227*66b5296fSJohn Baldwin if (discovery_controller_filtered(pg, client_sa, hostnqn, port)) 228*66b5296fSJohn Baldwin continue; 229*66b5296fSJohn Baldwin 230*66b5296fSJohn Baldwin for (const portal_up &portal : pg->portals()) { 231*66b5296fSJohn Baldwin if (portal->protocol() != portal_protocol::NVME_TCP) 232*66b5296fSJohn Baldwin continue; 233*66b5296fSJohn Baldwin 234*66b5296fSJohn Baldwin if (portal_uses_wildcard_address(portal.get()) && 235*66b5296fSJohn Baldwin portal->ai()->ai_family != client_sa->sa_family) 236*66b5296fSJohn Baldwin continue; 237*66b5296fSJohn Baldwin 238*66b5296fSJohn Baldwin struct nvme_discovery_log_entry entry; 239*66b5296fSJohn Baldwin if (init_discovery_log_entry(&entry, port->target(), 240*66b5296fSJohn Baldwin portal.get(), wildcard_host)) 241*66b5296fSJohn Baldwin discovery_log.append(&entry); 242*66b5296fSJohn Baldwin } 243*66b5296fSJohn Baldwin } 244*66b5296fSJohn Baldwin 245*66b5296fSJohn Baldwin return discovery_log; 246*66b5296fSJohn Baldwin } 247*66b5296fSJohn Baldwin 248*66b5296fSJohn Baldwin bool 249*66b5296fSJohn Baldwin discovery_controller::update_cc(uint32_t new_cc) 250*66b5296fSJohn Baldwin { 251*66b5296fSJohn Baldwin uint32_t changes; 252*66b5296fSJohn Baldwin 253*66b5296fSJohn Baldwin if (shutdown) 254*66b5296fSJohn Baldwin return false; 255*66b5296fSJohn Baldwin if (!nvmf_validate_cc(qp, cap, cc, new_cc)) 256*66b5296fSJohn Baldwin return false; 257*66b5296fSJohn Baldwin 258*66b5296fSJohn Baldwin changes = cc ^ new_cc; 259*66b5296fSJohn Baldwin cc = new_cc; 260*66b5296fSJohn Baldwin 261*66b5296fSJohn Baldwin /* Handle shutdown requests. */ 262*66b5296fSJohn Baldwin if (NVMEV(NVME_CC_REG_SHN, changes) != 0 && 263*66b5296fSJohn Baldwin NVMEV(NVME_CC_REG_SHN, new_cc) != 0) { 264*66b5296fSJohn Baldwin csts &= ~NVMEM(NVME_CSTS_REG_SHST); 265*66b5296fSJohn Baldwin csts |= NVMEF(NVME_CSTS_REG_SHST, NVME_SHST_COMPLETE); 266*66b5296fSJohn Baldwin shutdown = true; 267*66b5296fSJohn Baldwin } 268*66b5296fSJohn Baldwin 269*66b5296fSJohn Baldwin if (NVMEV(NVME_CC_REG_EN, changes) != 0) { 270*66b5296fSJohn Baldwin if (NVMEV(NVME_CC_REG_EN, new_cc) == 0) { 271*66b5296fSJohn Baldwin /* Controller reset. */ 272*66b5296fSJohn Baldwin csts = 0; 273*66b5296fSJohn Baldwin shutdown = true; 274*66b5296fSJohn Baldwin } else 275*66b5296fSJohn Baldwin csts |= NVMEF(NVME_CSTS_REG_RDY, 1); 276*66b5296fSJohn Baldwin } 277*66b5296fSJohn Baldwin return true; 278*66b5296fSJohn Baldwin } 279*66b5296fSJohn Baldwin 280*66b5296fSJohn Baldwin void 281*66b5296fSJohn Baldwin discovery_controller::handle_property_get(const struct nvmf_capsule *nc, 282*66b5296fSJohn Baldwin const struct nvmf_fabric_prop_get_cmd *pget) 283*66b5296fSJohn Baldwin { 284*66b5296fSJohn Baldwin struct nvmf_fabric_prop_get_rsp rsp; 285*66b5296fSJohn Baldwin 286*66b5296fSJohn Baldwin nvmf_init_cqe(&rsp, nc, 0); 287*66b5296fSJohn Baldwin 288*66b5296fSJohn Baldwin switch (le32toh(pget->ofst)) { 289*66b5296fSJohn Baldwin case NVMF_PROP_CAP: 290*66b5296fSJohn Baldwin if (pget->attrib.size != NVMF_PROP_SIZE_8) 291*66b5296fSJohn Baldwin goto error; 292*66b5296fSJohn Baldwin rsp.value.u64 = htole64(cap); 293*66b5296fSJohn Baldwin break; 294*66b5296fSJohn Baldwin case NVMF_PROP_VS: 295*66b5296fSJohn Baldwin if (pget->attrib.size != NVMF_PROP_SIZE_4) 296*66b5296fSJohn Baldwin goto error; 297*66b5296fSJohn Baldwin rsp.value.u32.low = htole32(vs); 298*66b5296fSJohn Baldwin break; 299*66b5296fSJohn Baldwin case NVMF_PROP_CC: 300*66b5296fSJohn Baldwin if (pget->attrib.size != NVMF_PROP_SIZE_4) 301*66b5296fSJohn Baldwin goto error; 302*66b5296fSJohn Baldwin rsp.value.u32.low = htole32(cc); 303*66b5296fSJohn Baldwin break; 304*66b5296fSJohn Baldwin case NVMF_PROP_CSTS: 305*66b5296fSJohn Baldwin if (pget->attrib.size != NVMF_PROP_SIZE_4) 306*66b5296fSJohn Baldwin goto error; 307*66b5296fSJohn Baldwin rsp.value.u32.low = htole32(csts); 308*66b5296fSJohn Baldwin break; 309*66b5296fSJohn Baldwin default: 310*66b5296fSJohn Baldwin goto error; 311*66b5296fSJohn Baldwin } 312*66b5296fSJohn Baldwin 313*66b5296fSJohn Baldwin nvmf_send_response(nc, &rsp); 314*66b5296fSJohn Baldwin return; 315*66b5296fSJohn Baldwin error: 316*66b5296fSJohn Baldwin nvmf_send_generic_error(nc, NVME_SC_INVALID_FIELD); 317*66b5296fSJohn Baldwin } 318*66b5296fSJohn Baldwin 319*66b5296fSJohn Baldwin void 320*66b5296fSJohn Baldwin discovery_controller::handle_property_set(const struct nvmf_capsule *nc, 321*66b5296fSJohn Baldwin const struct nvmf_fabric_prop_set_cmd *pset) 322*66b5296fSJohn Baldwin { 323*66b5296fSJohn Baldwin switch (le32toh(pset->ofst)) { 324*66b5296fSJohn Baldwin case NVMF_PROP_CC: 325*66b5296fSJohn Baldwin if (pset->attrib.size != NVMF_PROP_SIZE_4) 326*66b5296fSJohn Baldwin goto error; 327*66b5296fSJohn Baldwin if (!update_cc(le32toh(pset->value.u32.low))) 328*66b5296fSJohn Baldwin goto error; 329*66b5296fSJohn Baldwin break; 330*66b5296fSJohn Baldwin default: 331*66b5296fSJohn Baldwin goto error; 332*66b5296fSJohn Baldwin } 333*66b5296fSJohn Baldwin 334*66b5296fSJohn Baldwin nvmf_send_success(nc); 335*66b5296fSJohn Baldwin return; 336*66b5296fSJohn Baldwin error: 337*66b5296fSJohn Baldwin nvmf_send_generic_error(nc, NVME_SC_INVALID_FIELD); 338*66b5296fSJohn Baldwin } 339*66b5296fSJohn Baldwin 340*66b5296fSJohn Baldwin void 341*66b5296fSJohn Baldwin discovery_controller::handle_fabrics_command(const struct nvmf_capsule *nc, 342*66b5296fSJohn Baldwin const struct nvmf_fabric_cmd *fc) 343*66b5296fSJohn Baldwin { 344*66b5296fSJohn Baldwin switch (fc->fctype) { 345*66b5296fSJohn Baldwin case NVMF_FABRIC_COMMAND_PROPERTY_GET: 346*66b5296fSJohn Baldwin handle_property_get(nc, 347*66b5296fSJohn Baldwin (const struct nvmf_fabric_prop_get_cmd *)fc); 348*66b5296fSJohn Baldwin break; 349*66b5296fSJohn Baldwin case NVMF_FABRIC_COMMAND_PROPERTY_SET: 350*66b5296fSJohn Baldwin handle_property_set(nc, 351*66b5296fSJohn Baldwin (const struct nvmf_fabric_prop_set_cmd *)fc); 352*66b5296fSJohn Baldwin break; 353*66b5296fSJohn Baldwin case NVMF_FABRIC_COMMAND_CONNECT: 354*66b5296fSJohn Baldwin log_warnx("CONNECT command on connected queue"); 355*66b5296fSJohn Baldwin nvmf_send_generic_error(nc, NVME_SC_COMMAND_SEQUENCE_ERROR); 356*66b5296fSJohn Baldwin break; 357*66b5296fSJohn Baldwin case NVMF_FABRIC_COMMAND_DISCONNECT: 358*66b5296fSJohn Baldwin log_warnx("DISCONNECT command on admin queue"); 359*66b5296fSJohn Baldwin nvmf_send_error(nc, NVME_SCT_COMMAND_SPECIFIC, 360*66b5296fSJohn Baldwin NVMF_FABRIC_SC_INVALID_QUEUE_TYPE); 361*66b5296fSJohn Baldwin break; 362*66b5296fSJohn Baldwin default: 363*66b5296fSJohn Baldwin log_warnx("Unsupported fabrics command %#x", fc->fctype); 364*66b5296fSJohn Baldwin nvmf_send_generic_error(nc, NVME_SC_INVALID_OPCODE); 365*66b5296fSJohn Baldwin break; 366*66b5296fSJohn Baldwin } 367*66b5296fSJohn Baldwin } 368*66b5296fSJohn Baldwin 369*66b5296fSJohn Baldwin void 370*66b5296fSJohn Baldwin discovery_controller::handle_identify_command(const struct nvmf_capsule *nc, 371*66b5296fSJohn Baldwin const struct nvme_command *cmd) 372*66b5296fSJohn Baldwin { 373*66b5296fSJohn Baldwin uint8_t cns; 374*66b5296fSJohn Baldwin 375*66b5296fSJohn Baldwin cns = le32toh(cmd->cdw10) & 0xFF; 376*66b5296fSJohn Baldwin switch (cns) { 377*66b5296fSJohn Baldwin case 1: 378*66b5296fSJohn Baldwin break; 379*66b5296fSJohn Baldwin default: 380*66b5296fSJohn Baldwin log_warnx("Unsupported CNS %#x for IDENTIFY", cns); 381*66b5296fSJohn Baldwin goto error; 382*66b5296fSJohn Baldwin } 383*66b5296fSJohn Baldwin 384*66b5296fSJohn Baldwin nvmf_send_controller_data(nc, &cdata, sizeof(cdata)); 385*66b5296fSJohn Baldwin return; 386*66b5296fSJohn Baldwin error: 387*66b5296fSJohn Baldwin nvmf_send_generic_error(nc, NVME_SC_INVALID_FIELD); 388*66b5296fSJohn Baldwin } 389*66b5296fSJohn Baldwin 390*66b5296fSJohn Baldwin void 391*66b5296fSJohn Baldwin discovery_controller::handle_get_log_page_command(const struct nvmf_capsule *nc, 392*66b5296fSJohn Baldwin const struct nvme_command *cmd) 393*66b5296fSJohn Baldwin { 394*66b5296fSJohn Baldwin uint64_t offset; 395*66b5296fSJohn Baldwin uint32_t length; 396*66b5296fSJohn Baldwin 397*66b5296fSJohn Baldwin switch (nvmf_get_log_page_id(cmd)) { 398*66b5296fSJohn Baldwin case NVME_LOG_DISCOVERY: 399*66b5296fSJohn Baldwin break; 400*66b5296fSJohn Baldwin default: 401*66b5296fSJohn Baldwin log_warnx("Unsupported log page %u for discovery controller", 402*66b5296fSJohn Baldwin nvmf_get_log_page_id(cmd)); 403*66b5296fSJohn Baldwin goto error; 404*66b5296fSJohn Baldwin } 405*66b5296fSJohn Baldwin 406*66b5296fSJohn Baldwin offset = nvmf_get_log_page_offset(cmd); 407*66b5296fSJohn Baldwin if (offset >= discovery_log.length()) 408*66b5296fSJohn Baldwin goto error; 409*66b5296fSJohn Baldwin 410*66b5296fSJohn Baldwin length = nvmf_get_log_page_length(cmd); 411*66b5296fSJohn Baldwin if (length > discovery_log.length() - offset) 412*66b5296fSJohn Baldwin length = discovery_log.length() - offset; 413*66b5296fSJohn Baldwin 414*66b5296fSJohn Baldwin nvmf_send_controller_data(nc, discovery_log.data() + offset, length); 415*66b5296fSJohn Baldwin return; 416*66b5296fSJohn Baldwin error: 417*66b5296fSJohn Baldwin nvmf_send_generic_error(nc, NVME_SC_INVALID_FIELD); 418*66b5296fSJohn Baldwin } 419*66b5296fSJohn Baldwin 420*66b5296fSJohn Baldwin void 421*66b5296fSJohn Baldwin discovery_controller::handle_admin_commands() 422*66b5296fSJohn Baldwin { 423*66b5296fSJohn Baldwin for (;;) { 424*66b5296fSJohn Baldwin struct nvmf_capsule *nc; 425*66b5296fSJohn Baldwin int error = nvmf_controller_receive_capsule(qp, &nc); 426*66b5296fSJohn Baldwin if (error != 0) { 427*66b5296fSJohn Baldwin if (error != ECONNRESET) 428*66b5296fSJohn Baldwin log_warnc(error, 429*66b5296fSJohn Baldwin "Failed to read command capsule"); 430*66b5296fSJohn Baldwin break; 431*66b5296fSJohn Baldwin } 432*66b5296fSJohn Baldwin nvmf_capsule_up nc_guard(nc); 433*66b5296fSJohn Baldwin 434*66b5296fSJohn Baldwin const struct nvme_command *cmd = 435*66b5296fSJohn Baldwin (const struct nvme_command *)nvmf_capsule_sqe(nc); 436*66b5296fSJohn Baldwin 437*66b5296fSJohn Baldwin /* 438*66b5296fSJohn Baldwin * Only permit Fabrics commands while a controller is 439*66b5296fSJohn Baldwin * disabled. 440*66b5296fSJohn Baldwin */ 441*66b5296fSJohn Baldwin if (NVMEV(NVME_CC_REG_EN, cc) == 0 && 442*66b5296fSJohn Baldwin cmd->opc != NVME_OPC_FABRICS_COMMANDS) { 443*66b5296fSJohn Baldwin log_warnx("Unsupported admin opcode %#x while disabled\n", 444*66b5296fSJohn Baldwin cmd->opc); 445*66b5296fSJohn Baldwin nvmf_send_generic_error(nc, 446*66b5296fSJohn Baldwin NVME_SC_COMMAND_SEQUENCE_ERROR); 447*66b5296fSJohn Baldwin continue; 448*66b5296fSJohn Baldwin } 449*66b5296fSJohn Baldwin 450*66b5296fSJohn Baldwin switch (cmd->opc) { 451*66b5296fSJohn Baldwin case NVME_OPC_FABRICS_COMMANDS: 452*66b5296fSJohn Baldwin handle_fabrics_command(nc, 453*66b5296fSJohn Baldwin (const struct nvmf_fabric_cmd *)cmd); 454*66b5296fSJohn Baldwin break; 455*66b5296fSJohn Baldwin case NVME_OPC_IDENTIFY: 456*66b5296fSJohn Baldwin handle_identify_command(nc, cmd); 457*66b5296fSJohn Baldwin break; 458*66b5296fSJohn Baldwin case NVME_OPC_GET_LOG_PAGE: 459*66b5296fSJohn Baldwin handle_get_log_page_command(nc, cmd); 460*66b5296fSJohn Baldwin break; 461*66b5296fSJohn Baldwin default: 462*66b5296fSJohn Baldwin log_warnx("Unsupported admin opcode %#x", cmd->opc); 463*66b5296fSJohn Baldwin nvmf_send_generic_error(nc, NVME_SC_INVALID_OPCODE); 464*66b5296fSJohn Baldwin break; 465*66b5296fSJohn Baldwin } 466*66b5296fSJohn Baldwin } 467*66b5296fSJohn Baldwin } 468*66b5296fSJohn Baldwin 469*66b5296fSJohn Baldwin discovery_controller::discovery_controller(freebsd::fd_up fd, 470*66b5296fSJohn Baldwin struct nvmf_qpair *qp, const struct discovery_log &discovery_log) : 471*66b5296fSJohn Baldwin qp(qp), discovery_log(discovery_log), s(std::move(fd)) 472*66b5296fSJohn Baldwin { 473*66b5296fSJohn Baldwin nvmf_init_discovery_controller_data(qp, &cdata); 474*66b5296fSJohn Baldwin cap = nvmf_controller_cap(qp); 475*66b5296fSJohn Baldwin vs = cdata.ver; 476*66b5296fSJohn Baldwin } 477*66b5296fSJohn Baldwin 478*66b5296fSJohn Baldwin void 479*66b5296fSJohn Baldwin nvmf_discovery_portal::handle_connection(freebsd::fd_up fd, 480*66b5296fSJohn Baldwin const char *host __unused, const struct sockaddr *client_sa) 481*66b5296fSJohn Baldwin { 482*66b5296fSJohn Baldwin struct nvmf_qpair_params qparams; 483*66b5296fSJohn Baldwin memset(&qparams, 0, sizeof(qparams)); 484*66b5296fSJohn Baldwin qparams.tcp.fd = fd; 485*66b5296fSJohn Baldwin 486*66b5296fSJohn Baldwin struct nvmf_capsule *nc = NULL; 487*66b5296fSJohn Baldwin struct nvmf_fabric_connect_data data; 488*66b5296fSJohn Baldwin nvmf_qpair_up qp(nvmf_accept(association(), &qparams, &nc, &data)); 489*66b5296fSJohn Baldwin if (!qp) { 490*66b5296fSJohn Baldwin log_warnx("Failed to create NVMe discovery qpair: %s", 491*66b5296fSJohn Baldwin nvmf_association_error(association())); 492*66b5296fSJohn Baldwin return; 493*66b5296fSJohn Baldwin } 494*66b5296fSJohn Baldwin nvmf_capsule_up nc_guard(nc); 495*66b5296fSJohn Baldwin 496*66b5296fSJohn Baldwin if (strncmp((char *)data.subnqn, NVMF_DISCOVERY_NQN, 497*66b5296fSJohn Baldwin sizeof(data.subnqn)) != 0) { 498*66b5296fSJohn Baldwin log_warnx("Discovery NVMe qpair with invalid SubNQN: %.*s", 499*66b5296fSJohn Baldwin (int)sizeof(data.subnqn), data.subnqn); 500*66b5296fSJohn Baldwin nvmf_connect_invalid_parameters(nc, true, 501*66b5296fSJohn Baldwin offsetof(struct nvmf_fabric_connect_data, subnqn)); 502*66b5296fSJohn Baldwin return; 503*66b5296fSJohn Baldwin } 504*66b5296fSJohn Baldwin 505*66b5296fSJohn Baldwin /* Just use a controller ID of 1 for all discovery controllers. */ 506*66b5296fSJohn Baldwin int error = nvmf_finish_accept(nc, 1); 507*66b5296fSJohn Baldwin if (error != 0) { 508*66b5296fSJohn Baldwin log_warnc(error, "Failed to send NVMe CONNECT reponse"); 509*66b5296fSJohn Baldwin return; 510*66b5296fSJohn Baldwin } 511*66b5296fSJohn Baldwin nc_guard.reset(); 512*66b5296fSJohn Baldwin 513*66b5296fSJohn Baldwin discovery_log discovery_log = build_discovery_log_page(portal_group(), 514*66b5296fSJohn Baldwin fd, client_sa, data); 515*66b5296fSJohn Baldwin 516*66b5296fSJohn Baldwin discovery_controller controller(std::move(fd), qp.get(), discovery_log); 517*66b5296fSJohn Baldwin controller.handle_admin_commands(); 518*66b5296fSJohn Baldwin } 519