166b5296fSJohn Baldwin /*-
266b5296fSJohn Baldwin * SPDX-License-Identifier: BSD-2-Clause
366b5296fSJohn Baldwin *
466b5296fSJohn Baldwin * Copyright (c) 2023-2025 Chelsio Communications, Inc.
566b5296fSJohn Baldwin * Written by: John Baldwin <jhb@FreeBSD.org>
666b5296fSJohn Baldwin */
766b5296fSJohn Baldwin
866b5296fSJohn Baldwin #include <assert.h>
966b5296fSJohn Baldwin #include <errno.h>
1066b5296fSJohn Baldwin #include <netdb.h>
1166b5296fSJohn Baldwin #include <libiscsiutil.h>
1266b5296fSJohn Baldwin #include <libnvmf.h>
1366b5296fSJohn Baldwin #include <stdlib.h>
1466b5296fSJohn Baldwin #include <string.h>
1566b5296fSJohn Baldwin #include <unistd.h>
1666b5296fSJohn Baldwin #include <netinet/in.h>
1766b5296fSJohn Baldwin
1866b5296fSJohn Baldwin #include "ctld.hh"
1966b5296fSJohn Baldwin #include "nvmf.hh"
2066b5296fSJohn Baldwin
2166b5296fSJohn Baldwin struct discovery_log {
2266b5296fSJohn Baldwin discovery_log(const struct portal_group *pg);
2366b5296fSJohn Baldwin
datadiscovery_log2466b5296fSJohn Baldwin const char *data() const { return buf.data(); }
lengthdiscovery_log2566b5296fSJohn Baldwin size_t length() const { return buf.size(); }
2666b5296fSJohn Baldwin
2766b5296fSJohn Baldwin void append(const struct nvme_discovery_log_entry *entry);
2866b5296fSJohn Baldwin
2966b5296fSJohn Baldwin private:
headerdiscovery_log3066b5296fSJohn Baldwin struct nvme_discovery_log *header()
3166b5296fSJohn Baldwin { return reinterpret_cast<struct nvme_discovery_log *>(buf.data()); }
3266b5296fSJohn Baldwin
3366b5296fSJohn Baldwin std::vector<char> buf;
3466b5296fSJohn Baldwin };
3566b5296fSJohn Baldwin
3666b5296fSJohn Baldwin struct discovery_controller {
3766b5296fSJohn Baldwin discovery_controller(freebsd::fd_up s, struct nvmf_qpair *qp,
3866b5296fSJohn Baldwin const discovery_log &discovery_log);
3966b5296fSJohn Baldwin
4066b5296fSJohn Baldwin void handle_admin_commands();
4166b5296fSJohn Baldwin private:
4266b5296fSJohn Baldwin bool update_cc(uint32_t new_cc);
4366b5296fSJohn Baldwin void handle_property_get(const struct nvmf_capsule *nc,
4466b5296fSJohn Baldwin const struct nvmf_fabric_prop_get_cmd *pget);
4566b5296fSJohn Baldwin void handle_property_set(const struct nvmf_capsule *nc,
4666b5296fSJohn Baldwin const struct nvmf_fabric_prop_set_cmd *pset);
4766b5296fSJohn Baldwin void handle_fabrics_command(const struct nvmf_capsule *nc,
4866b5296fSJohn Baldwin const struct nvmf_fabric_cmd *cmd);
4966b5296fSJohn Baldwin void handle_identify_command(const struct nvmf_capsule *nc,
5066b5296fSJohn Baldwin const struct nvme_command *cmd);
5166b5296fSJohn Baldwin void handle_get_log_page_command(const struct nvmf_capsule *nc,
5266b5296fSJohn Baldwin const struct nvme_command *cmd);
5366b5296fSJohn Baldwin
5466b5296fSJohn Baldwin struct nvmf_qpair *qp;
5566b5296fSJohn Baldwin
5666b5296fSJohn Baldwin uint64_t cap = 0;
5766b5296fSJohn Baldwin uint32_t vs = 0;
5866b5296fSJohn Baldwin uint32_t cc = 0;
5966b5296fSJohn Baldwin uint32_t csts = 0;
6066b5296fSJohn Baldwin
6166b5296fSJohn Baldwin bool shutdown = false;
6266b5296fSJohn Baldwin
6366b5296fSJohn Baldwin struct nvme_controller_data cdata;
6466b5296fSJohn Baldwin
65*d21b5139SJohn Baldwin const struct discovery_log &discovery_log;
6666b5296fSJohn Baldwin freebsd::fd_up s;
6766b5296fSJohn Baldwin };
6866b5296fSJohn Baldwin
discovery_log(const struct portal_group * pg)6966b5296fSJohn Baldwin discovery_log::discovery_log(const struct portal_group *pg) :
7066b5296fSJohn Baldwin buf(sizeof(nvme_discovery_log))
7166b5296fSJohn Baldwin {
7266b5296fSJohn Baldwin struct nvme_discovery_log *log = header();
7366b5296fSJohn Baldwin
7466b5296fSJohn Baldwin log->genctr = htole32(pg->conf()->genctr());
7566b5296fSJohn Baldwin log->recfmt = 0;
7666b5296fSJohn Baldwin }
7766b5296fSJohn Baldwin
7866b5296fSJohn Baldwin void
append(const struct nvme_discovery_log_entry * entry)7966b5296fSJohn Baldwin discovery_log::append(const struct nvme_discovery_log_entry *entry)
8066b5296fSJohn Baldwin {
8166b5296fSJohn Baldwin const char *cp = reinterpret_cast<const char *>(entry);
8266b5296fSJohn Baldwin buf.insert(buf.end(), cp, cp + sizeof(*entry));
8366b5296fSJohn Baldwin
8466b5296fSJohn Baldwin struct nvme_discovery_log *log = header();
8566b5296fSJohn Baldwin log->numrec = htole32(le32toh(log->numrec) + 1);
8666b5296fSJohn Baldwin }
8766b5296fSJohn Baldwin
8866b5296fSJohn Baldwin static bool
discovery_controller_filtered(const struct portal_group * pg,const struct sockaddr * client_sa,std::string_view hostnqn,const struct port * port)8966b5296fSJohn Baldwin discovery_controller_filtered(const struct portal_group *pg,
9066b5296fSJohn Baldwin const struct sockaddr *client_sa, std::string_view hostnqn,
9166b5296fSJohn Baldwin const struct port *port)
9266b5296fSJohn Baldwin {
9366b5296fSJohn Baldwin const struct target *targ = port->target();
9466b5296fSJohn Baldwin const struct auth_group *ag = port->auth_group();
9566b5296fSJohn Baldwin if (ag == nullptr)
9666b5296fSJohn Baldwin ag = targ->auth_group();
9766b5296fSJohn Baldwin
9866b5296fSJohn Baldwin assert(pg->discovery_filter() != discovery_filter::UNKNOWN);
9966b5296fSJohn Baldwin
10066b5296fSJohn Baldwin if (pg->discovery_filter() >= discovery_filter::PORTAL &&
10166b5296fSJohn Baldwin !ag->host_permitted(client_sa)) {
10266b5296fSJohn Baldwin log_debugx("host address does not match addresses "
10366b5296fSJohn Baldwin "allowed for controller \"%s\"; skipping", targ->name());
10466b5296fSJohn Baldwin return true;
10566b5296fSJohn Baldwin }
10666b5296fSJohn Baldwin
10766b5296fSJohn Baldwin if (pg->discovery_filter() >= discovery_filter::PORTAL_NAME &&
10866b5296fSJohn Baldwin !ag->host_permitted(hostnqn) != 0) {
10966b5296fSJohn Baldwin log_debugx("HostNQN does not match NQNs "
11066b5296fSJohn Baldwin "allowed for controller \"%s\"; skipping", targ->name());
11166b5296fSJohn Baldwin return true;
11266b5296fSJohn Baldwin }
11366b5296fSJohn Baldwin
11466b5296fSJohn Baldwin /* XXX: auth not yet implemented for NVMe */
11566b5296fSJohn Baldwin
11666b5296fSJohn Baldwin return false;
11766b5296fSJohn Baldwin }
11866b5296fSJohn Baldwin
11966b5296fSJohn Baldwin static bool
portal_uses_wildcard_address(const struct portal * p)12066b5296fSJohn Baldwin portal_uses_wildcard_address(const struct portal *p)
12166b5296fSJohn Baldwin {
12266b5296fSJohn Baldwin const struct addrinfo *ai = p->ai();
12366b5296fSJohn Baldwin
12466b5296fSJohn Baldwin switch (ai->ai_family) {
12566b5296fSJohn Baldwin case AF_INET:
12666b5296fSJohn Baldwin {
12766b5296fSJohn Baldwin const struct sockaddr_in *sin;
12866b5296fSJohn Baldwin
12966b5296fSJohn Baldwin sin = (const struct sockaddr_in *)ai->ai_addr;
13066b5296fSJohn Baldwin return sin->sin_addr.s_addr == htonl(INADDR_ANY);
13166b5296fSJohn Baldwin }
13266b5296fSJohn Baldwin case AF_INET6:
13366b5296fSJohn Baldwin {
13466b5296fSJohn Baldwin const struct sockaddr_in6 *sin6;
13566b5296fSJohn Baldwin
13666b5296fSJohn Baldwin sin6 = (const struct sockaddr_in6 *)ai->ai_addr;
13766b5296fSJohn Baldwin return memcmp(&sin6->sin6_addr, &in6addr_any,
13866b5296fSJohn Baldwin sizeof(in6addr_any)) == 0;
13966b5296fSJohn Baldwin }
14066b5296fSJohn Baldwin default:
14166b5296fSJohn Baldwin __assert_unreachable();
14266b5296fSJohn Baldwin }
14366b5296fSJohn Baldwin }
14466b5296fSJohn Baldwin
14566b5296fSJohn Baldwin static bool
init_discovery_log_entry(struct nvme_discovery_log_entry * entry,const struct target * target,const struct portal * portal,const char * wildcard_host)14666b5296fSJohn Baldwin init_discovery_log_entry(struct nvme_discovery_log_entry *entry,
14766b5296fSJohn Baldwin const struct target *target, const struct portal *portal,
14866b5296fSJohn Baldwin const char *wildcard_host)
14966b5296fSJohn Baldwin {
15066b5296fSJohn Baldwin /*
15166b5296fSJohn Baldwin * The TCP port for I/O controllers might not be fixed, so
15266b5296fSJohn Baldwin * fetch the sockaddr of the socket to determine which port
15366b5296fSJohn Baldwin * the kernel chose.
15466b5296fSJohn Baldwin */
15566b5296fSJohn Baldwin struct sockaddr_storage ss;
15666b5296fSJohn Baldwin socklen_t len = sizeof(ss);
15766b5296fSJohn Baldwin if (getsockname(portal->socket(), (struct sockaddr *)&ss, &len) == -1) {
15866b5296fSJohn Baldwin log_warn("Failed getsockname building discovery log entry");
15966b5296fSJohn Baldwin return false;
16066b5296fSJohn Baldwin }
16166b5296fSJohn Baldwin
16266b5296fSJohn Baldwin const struct nvmf_association_params *aparams =
16366b5296fSJohn Baldwin static_cast<const nvmf_portal *>(portal)->aparams();
16466b5296fSJohn Baldwin
16566b5296fSJohn Baldwin memset(entry, 0, sizeof(*entry));
16666b5296fSJohn Baldwin entry->trtype = NVMF_TRTYPE_TCP;
16766b5296fSJohn Baldwin int error = getnameinfo((struct sockaddr *)&ss, len,
16866b5296fSJohn Baldwin (char *)entry->traddr, sizeof(entry->traddr),
16966b5296fSJohn Baldwin (char *)entry->trsvcid, sizeof(entry->trsvcid),
17066b5296fSJohn Baldwin NI_NUMERICHOST | NI_NUMERICSERV);
17166b5296fSJohn Baldwin if (error != 0) {
17266b5296fSJohn Baldwin log_warnx("Failed getnameinfo building discovery log entry: %s",
17366b5296fSJohn Baldwin gai_strerror(error));
17466b5296fSJohn Baldwin return false;
17566b5296fSJohn Baldwin }
17666b5296fSJohn Baldwin
17766b5296fSJohn Baldwin if (portal_uses_wildcard_address(portal))
17866b5296fSJohn Baldwin strncpy((char *)entry->traddr, wildcard_host,
17966b5296fSJohn Baldwin sizeof(entry->traddr));
18066b5296fSJohn Baldwin switch (portal->ai()->ai_family) {
18166b5296fSJohn Baldwin case AF_INET:
18266b5296fSJohn Baldwin entry->adrfam = NVMF_ADRFAM_IPV4;
18366b5296fSJohn Baldwin break;
18466b5296fSJohn Baldwin case AF_INET6:
18566b5296fSJohn Baldwin entry->adrfam = NVMF_ADRFAM_IPV6;
18666b5296fSJohn Baldwin break;
18766b5296fSJohn Baldwin default:
18866b5296fSJohn Baldwin __assert_unreachable();
18966b5296fSJohn Baldwin }
19066b5296fSJohn Baldwin entry->subtype = NVMF_SUBTYPE_NVME;
19166b5296fSJohn Baldwin if (!aparams->sq_flow_control)
19266b5296fSJohn Baldwin entry->treq |= (1 << 2);
19366b5296fSJohn Baldwin entry->portid = htole16(portal->portal_group()->tag());
19466b5296fSJohn Baldwin entry->cntlid = htole16(NVMF_CNTLID_DYNAMIC);
19566b5296fSJohn Baldwin entry->aqsz = aparams->max_admin_qsize;
19666b5296fSJohn Baldwin strncpy((char *)entry->subnqn, target->name(), sizeof(entry->subnqn));
19766b5296fSJohn Baldwin return true;
19866b5296fSJohn Baldwin }
19966b5296fSJohn Baldwin
20066b5296fSJohn Baldwin static discovery_log
build_discovery_log_page(const struct portal_group * pg,int fd,const struct sockaddr * client_sa,const struct nvmf_fabric_connect_data & data)20166b5296fSJohn Baldwin build_discovery_log_page(const struct portal_group *pg, int fd,
20266b5296fSJohn Baldwin const struct sockaddr *client_sa,
20366b5296fSJohn Baldwin const struct nvmf_fabric_connect_data &data)
20466b5296fSJohn Baldwin {
20566b5296fSJohn Baldwin discovery_log discovery_log(pg);
20666b5296fSJohn Baldwin
20766b5296fSJohn Baldwin struct sockaddr_storage ss;
20866b5296fSJohn Baldwin socklen_t len = sizeof(ss);
20966b5296fSJohn Baldwin if (getsockname(fd, (struct sockaddr *)&ss, &len) == -1) {
21066b5296fSJohn Baldwin log_warn("build_discovery_log_page: getsockname");
21166b5296fSJohn Baldwin return discovery_log;
21266b5296fSJohn Baldwin }
21366b5296fSJohn Baldwin
21466b5296fSJohn Baldwin char wildcard_host[NI_MAXHOST];
21566b5296fSJohn Baldwin int error = getnameinfo((struct sockaddr *)&ss, len, wildcard_host,
21666b5296fSJohn Baldwin sizeof(wildcard_host), NULL, 0, NI_NUMERICHOST);
21766b5296fSJohn Baldwin if (error != 0) {
21866b5296fSJohn Baldwin log_warnx("build_discovery_log_page: getnameinfo: %s",
21966b5296fSJohn Baldwin gai_strerror(error));
22066b5296fSJohn Baldwin return discovery_log;
22166b5296fSJohn Baldwin }
22266b5296fSJohn Baldwin
22366b5296fSJohn Baldwin const char *nqn = (const char *)data.hostnqn;
22466b5296fSJohn Baldwin std::string hostnqn(nqn, strnlen(nqn, sizeof(data.hostnqn)));
22566b5296fSJohn Baldwin for (const auto &kv : pg->ports()) {
22666b5296fSJohn Baldwin const struct port *port = kv.second;
22766b5296fSJohn Baldwin if (discovery_controller_filtered(pg, client_sa, hostnqn, port))
22866b5296fSJohn Baldwin continue;
22966b5296fSJohn Baldwin
23066b5296fSJohn Baldwin for (const portal_up &portal : pg->portals()) {
23166b5296fSJohn Baldwin if (portal->protocol() != portal_protocol::NVME_TCP)
23266b5296fSJohn Baldwin continue;
23366b5296fSJohn Baldwin
23466b5296fSJohn Baldwin if (portal_uses_wildcard_address(portal.get()) &&
23566b5296fSJohn Baldwin portal->ai()->ai_family != client_sa->sa_family)
23666b5296fSJohn Baldwin continue;
23766b5296fSJohn Baldwin
23866b5296fSJohn Baldwin struct nvme_discovery_log_entry entry;
23966b5296fSJohn Baldwin if (init_discovery_log_entry(&entry, port->target(),
24066b5296fSJohn Baldwin portal.get(), wildcard_host))
24166b5296fSJohn Baldwin discovery_log.append(&entry);
24266b5296fSJohn Baldwin }
24366b5296fSJohn Baldwin }
24466b5296fSJohn Baldwin
24566b5296fSJohn Baldwin return discovery_log;
24666b5296fSJohn Baldwin }
24766b5296fSJohn Baldwin
24866b5296fSJohn Baldwin bool
update_cc(uint32_t new_cc)24966b5296fSJohn Baldwin discovery_controller::update_cc(uint32_t new_cc)
25066b5296fSJohn Baldwin {
25166b5296fSJohn Baldwin uint32_t changes;
25266b5296fSJohn Baldwin
25366b5296fSJohn Baldwin if (shutdown)
25466b5296fSJohn Baldwin return false;
25566b5296fSJohn Baldwin if (!nvmf_validate_cc(qp, cap, cc, new_cc))
25666b5296fSJohn Baldwin return false;
25766b5296fSJohn Baldwin
25866b5296fSJohn Baldwin changes = cc ^ new_cc;
25966b5296fSJohn Baldwin cc = new_cc;
26066b5296fSJohn Baldwin
26166b5296fSJohn Baldwin /* Handle shutdown requests. */
26266b5296fSJohn Baldwin if (NVMEV(NVME_CC_REG_SHN, changes) != 0 &&
26366b5296fSJohn Baldwin NVMEV(NVME_CC_REG_SHN, new_cc) != 0) {
26466b5296fSJohn Baldwin csts &= ~NVMEM(NVME_CSTS_REG_SHST);
26566b5296fSJohn Baldwin csts |= NVMEF(NVME_CSTS_REG_SHST, NVME_SHST_COMPLETE);
26666b5296fSJohn Baldwin shutdown = true;
26766b5296fSJohn Baldwin }
26866b5296fSJohn Baldwin
26966b5296fSJohn Baldwin if (NVMEV(NVME_CC_REG_EN, changes) != 0) {
27066b5296fSJohn Baldwin if (NVMEV(NVME_CC_REG_EN, new_cc) == 0) {
27166b5296fSJohn Baldwin /* Controller reset. */
27266b5296fSJohn Baldwin csts = 0;
27366b5296fSJohn Baldwin shutdown = true;
27466b5296fSJohn Baldwin } else
27566b5296fSJohn Baldwin csts |= NVMEF(NVME_CSTS_REG_RDY, 1);
27666b5296fSJohn Baldwin }
27766b5296fSJohn Baldwin return true;
27866b5296fSJohn Baldwin }
27966b5296fSJohn Baldwin
28066b5296fSJohn Baldwin void
handle_property_get(const struct nvmf_capsule * nc,const struct nvmf_fabric_prop_get_cmd * pget)28166b5296fSJohn Baldwin discovery_controller::handle_property_get(const struct nvmf_capsule *nc,
28266b5296fSJohn Baldwin const struct nvmf_fabric_prop_get_cmd *pget)
28366b5296fSJohn Baldwin {
28466b5296fSJohn Baldwin struct nvmf_fabric_prop_get_rsp rsp;
28566b5296fSJohn Baldwin
28666b5296fSJohn Baldwin nvmf_init_cqe(&rsp, nc, 0);
28766b5296fSJohn Baldwin
28866b5296fSJohn Baldwin switch (le32toh(pget->ofst)) {
28966b5296fSJohn Baldwin case NVMF_PROP_CAP:
29066b5296fSJohn Baldwin if (pget->attrib.size != NVMF_PROP_SIZE_8)
29166b5296fSJohn Baldwin goto error;
29266b5296fSJohn Baldwin rsp.value.u64 = htole64(cap);
29366b5296fSJohn Baldwin break;
29466b5296fSJohn Baldwin case NVMF_PROP_VS:
29566b5296fSJohn Baldwin if (pget->attrib.size != NVMF_PROP_SIZE_4)
29666b5296fSJohn Baldwin goto error;
29766b5296fSJohn Baldwin rsp.value.u32.low = htole32(vs);
29866b5296fSJohn Baldwin break;
29966b5296fSJohn Baldwin case NVMF_PROP_CC:
30066b5296fSJohn Baldwin if (pget->attrib.size != NVMF_PROP_SIZE_4)
30166b5296fSJohn Baldwin goto error;
30266b5296fSJohn Baldwin rsp.value.u32.low = htole32(cc);
30366b5296fSJohn Baldwin break;
30466b5296fSJohn Baldwin case NVMF_PROP_CSTS:
30566b5296fSJohn Baldwin if (pget->attrib.size != NVMF_PROP_SIZE_4)
30666b5296fSJohn Baldwin goto error;
30766b5296fSJohn Baldwin rsp.value.u32.low = htole32(csts);
30866b5296fSJohn Baldwin break;
30966b5296fSJohn Baldwin default:
31066b5296fSJohn Baldwin goto error;
31166b5296fSJohn Baldwin }
31266b5296fSJohn Baldwin
31366b5296fSJohn Baldwin nvmf_send_response(nc, &rsp);
31466b5296fSJohn Baldwin return;
31566b5296fSJohn Baldwin error:
31666b5296fSJohn Baldwin nvmf_send_generic_error(nc, NVME_SC_INVALID_FIELD);
31766b5296fSJohn Baldwin }
31866b5296fSJohn Baldwin
31966b5296fSJohn Baldwin void
handle_property_set(const struct nvmf_capsule * nc,const struct nvmf_fabric_prop_set_cmd * pset)32066b5296fSJohn Baldwin discovery_controller::handle_property_set(const struct nvmf_capsule *nc,
32166b5296fSJohn Baldwin const struct nvmf_fabric_prop_set_cmd *pset)
32266b5296fSJohn Baldwin {
32366b5296fSJohn Baldwin switch (le32toh(pset->ofst)) {
32466b5296fSJohn Baldwin case NVMF_PROP_CC:
32566b5296fSJohn Baldwin if (pset->attrib.size != NVMF_PROP_SIZE_4)
32666b5296fSJohn Baldwin goto error;
32766b5296fSJohn Baldwin if (!update_cc(le32toh(pset->value.u32.low)))
32866b5296fSJohn Baldwin goto error;
32966b5296fSJohn Baldwin break;
33066b5296fSJohn Baldwin default:
33166b5296fSJohn Baldwin goto error;
33266b5296fSJohn Baldwin }
33366b5296fSJohn Baldwin
33466b5296fSJohn Baldwin nvmf_send_success(nc);
33566b5296fSJohn Baldwin return;
33666b5296fSJohn Baldwin error:
33766b5296fSJohn Baldwin nvmf_send_generic_error(nc, NVME_SC_INVALID_FIELD);
33866b5296fSJohn Baldwin }
33966b5296fSJohn Baldwin
34066b5296fSJohn Baldwin void
handle_fabrics_command(const struct nvmf_capsule * nc,const struct nvmf_fabric_cmd * fc)34166b5296fSJohn Baldwin discovery_controller::handle_fabrics_command(const struct nvmf_capsule *nc,
34266b5296fSJohn Baldwin const struct nvmf_fabric_cmd *fc)
34366b5296fSJohn Baldwin {
34466b5296fSJohn Baldwin switch (fc->fctype) {
34566b5296fSJohn Baldwin case NVMF_FABRIC_COMMAND_PROPERTY_GET:
34666b5296fSJohn Baldwin handle_property_get(nc,
34766b5296fSJohn Baldwin (const struct nvmf_fabric_prop_get_cmd *)fc);
34866b5296fSJohn Baldwin break;
34966b5296fSJohn Baldwin case NVMF_FABRIC_COMMAND_PROPERTY_SET:
35066b5296fSJohn Baldwin handle_property_set(nc,
35166b5296fSJohn Baldwin (const struct nvmf_fabric_prop_set_cmd *)fc);
35266b5296fSJohn Baldwin break;
35366b5296fSJohn Baldwin case NVMF_FABRIC_COMMAND_CONNECT:
35466b5296fSJohn Baldwin log_warnx("CONNECT command on connected queue");
35566b5296fSJohn Baldwin nvmf_send_generic_error(nc, NVME_SC_COMMAND_SEQUENCE_ERROR);
35666b5296fSJohn Baldwin break;
35766b5296fSJohn Baldwin case NVMF_FABRIC_COMMAND_DISCONNECT:
35866b5296fSJohn Baldwin log_warnx("DISCONNECT command on admin queue");
35966b5296fSJohn Baldwin nvmf_send_error(nc, NVME_SCT_COMMAND_SPECIFIC,
36066b5296fSJohn Baldwin NVMF_FABRIC_SC_INVALID_QUEUE_TYPE);
36166b5296fSJohn Baldwin break;
36266b5296fSJohn Baldwin default:
36366b5296fSJohn Baldwin log_warnx("Unsupported fabrics command %#x", fc->fctype);
36466b5296fSJohn Baldwin nvmf_send_generic_error(nc, NVME_SC_INVALID_OPCODE);
36566b5296fSJohn Baldwin break;
36666b5296fSJohn Baldwin }
36766b5296fSJohn Baldwin }
36866b5296fSJohn Baldwin
36966b5296fSJohn Baldwin void
handle_identify_command(const struct nvmf_capsule * nc,const struct nvme_command * cmd)37066b5296fSJohn Baldwin discovery_controller::handle_identify_command(const struct nvmf_capsule *nc,
37166b5296fSJohn Baldwin const struct nvme_command *cmd)
37266b5296fSJohn Baldwin {
37366b5296fSJohn Baldwin uint8_t cns;
37466b5296fSJohn Baldwin
37566b5296fSJohn Baldwin cns = le32toh(cmd->cdw10) & 0xFF;
37666b5296fSJohn Baldwin switch (cns) {
37766b5296fSJohn Baldwin case 1:
37866b5296fSJohn Baldwin break;
37966b5296fSJohn Baldwin default:
38066b5296fSJohn Baldwin log_warnx("Unsupported CNS %#x for IDENTIFY", cns);
38166b5296fSJohn Baldwin goto error;
38266b5296fSJohn Baldwin }
38366b5296fSJohn Baldwin
38466b5296fSJohn Baldwin nvmf_send_controller_data(nc, &cdata, sizeof(cdata));
38566b5296fSJohn Baldwin return;
38666b5296fSJohn Baldwin error:
38766b5296fSJohn Baldwin nvmf_send_generic_error(nc, NVME_SC_INVALID_FIELD);
38866b5296fSJohn Baldwin }
38966b5296fSJohn Baldwin
39066b5296fSJohn Baldwin void
handle_get_log_page_command(const struct nvmf_capsule * nc,const struct nvme_command * cmd)39166b5296fSJohn Baldwin discovery_controller::handle_get_log_page_command(const struct nvmf_capsule *nc,
39266b5296fSJohn Baldwin const struct nvme_command *cmd)
39366b5296fSJohn Baldwin {
39466b5296fSJohn Baldwin uint64_t offset;
39566b5296fSJohn Baldwin uint32_t length;
39666b5296fSJohn Baldwin
39766b5296fSJohn Baldwin switch (nvmf_get_log_page_id(cmd)) {
39866b5296fSJohn Baldwin case NVME_LOG_DISCOVERY:
39966b5296fSJohn Baldwin break;
40066b5296fSJohn Baldwin default:
40166b5296fSJohn Baldwin log_warnx("Unsupported log page %u for discovery controller",
40266b5296fSJohn Baldwin nvmf_get_log_page_id(cmd));
40366b5296fSJohn Baldwin goto error;
40466b5296fSJohn Baldwin }
40566b5296fSJohn Baldwin
40666b5296fSJohn Baldwin offset = nvmf_get_log_page_offset(cmd);
40766b5296fSJohn Baldwin if (offset >= discovery_log.length())
40866b5296fSJohn Baldwin goto error;
40966b5296fSJohn Baldwin
41066b5296fSJohn Baldwin length = nvmf_get_log_page_length(cmd);
41166b5296fSJohn Baldwin if (length > discovery_log.length() - offset)
41266b5296fSJohn Baldwin length = discovery_log.length() - offset;
41366b5296fSJohn Baldwin
41466b5296fSJohn Baldwin nvmf_send_controller_data(nc, discovery_log.data() + offset, length);
41566b5296fSJohn Baldwin return;
41666b5296fSJohn Baldwin error:
41766b5296fSJohn Baldwin nvmf_send_generic_error(nc, NVME_SC_INVALID_FIELD);
41866b5296fSJohn Baldwin }
41966b5296fSJohn Baldwin
42066b5296fSJohn Baldwin void
handle_admin_commands()42166b5296fSJohn Baldwin discovery_controller::handle_admin_commands()
42266b5296fSJohn Baldwin {
42366b5296fSJohn Baldwin for (;;) {
42466b5296fSJohn Baldwin struct nvmf_capsule *nc;
42566b5296fSJohn Baldwin int error = nvmf_controller_receive_capsule(qp, &nc);
42666b5296fSJohn Baldwin if (error != 0) {
42766b5296fSJohn Baldwin if (error != ECONNRESET)
42866b5296fSJohn Baldwin log_warnc(error,
42966b5296fSJohn Baldwin "Failed to read command capsule");
43066b5296fSJohn Baldwin break;
43166b5296fSJohn Baldwin }
43266b5296fSJohn Baldwin nvmf_capsule_up nc_guard(nc);
43366b5296fSJohn Baldwin
43466b5296fSJohn Baldwin const struct nvme_command *cmd =
43566b5296fSJohn Baldwin (const struct nvme_command *)nvmf_capsule_sqe(nc);
43666b5296fSJohn Baldwin
43766b5296fSJohn Baldwin /*
43866b5296fSJohn Baldwin * Only permit Fabrics commands while a controller is
43966b5296fSJohn Baldwin * disabled.
44066b5296fSJohn Baldwin */
44166b5296fSJohn Baldwin if (NVMEV(NVME_CC_REG_EN, cc) == 0 &&
44266b5296fSJohn Baldwin cmd->opc != NVME_OPC_FABRICS_COMMANDS) {
44366b5296fSJohn Baldwin log_warnx("Unsupported admin opcode %#x while disabled\n",
44466b5296fSJohn Baldwin cmd->opc);
44566b5296fSJohn Baldwin nvmf_send_generic_error(nc,
44666b5296fSJohn Baldwin NVME_SC_COMMAND_SEQUENCE_ERROR);
44766b5296fSJohn Baldwin continue;
44866b5296fSJohn Baldwin }
44966b5296fSJohn Baldwin
45066b5296fSJohn Baldwin switch (cmd->opc) {
45166b5296fSJohn Baldwin case NVME_OPC_FABRICS_COMMANDS:
45266b5296fSJohn Baldwin handle_fabrics_command(nc,
45366b5296fSJohn Baldwin (const struct nvmf_fabric_cmd *)cmd);
45466b5296fSJohn Baldwin break;
45566b5296fSJohn Baldwin case NVME_OPC_IDENTIFY:
45666b5296fSJohn Baldwin handle_identify_command(nc, cmd);
45766b5296fSJohn Baldwin break;
45866b5296fSJohn Baldwin case NVME_OPC_GET_LOG_PAGE:
45966b5296fSJohn Baldwin handle_get_log_page_command(nc, cmd);
46066b5296fSJohn Baldwin break;
46166b5296fSJohn Baldwin default:
46266b5296fSJohn Baldwin log_warnx("Unsupported admin opcode %#x", cmd->opc);
46366b5296fSJohn Baldwin nvmf_send_generic_error(nc, NVME_SC_INVALID_OPCODE);
46466b5296fSJohn Baldwin break;
46566b5296fSJohn Baldwin }
46666b5296fSJohn Baldwin }
46766b5296fSJohn Baldwin }
46866b5296fSJohn Baldwin
discovery_controller(freebsd::fd_up fd,struct nvmf_qpair * qp,const struct discovery_log & discovery_log)46966b5296fSJohn Baldwin discovery_controller::discovery_controller(freebsd::fd_up fd,
47066b5296fSJohn Baldwin struct nvmf_qpair *qp, const struct discovery_log &discovery_log) :
47166b5296fSJohn Baldwin qp(qp), discovery_log(discovery_log), s(std::move(fd))
47266b5296fSJohn Baldwin {
47366b5296fSJohn Baldwin nvmf_init_discovery_controller_data(qp, &cdata);
47466b5296fSJohn Baldwin cap = nvmf_controller_cap(qp);
47566b5296fSJohn Baldwin vs = cdata.ver;
47666b5296fSJohn Baldwin }
47766b5296fSJohn Baldwin
47866b5296fSJohn Baldwin void
handle_connection(freebsd::fd_up fd,const char * host __unused,const struct sockaddr * client_sa)47966b5296fSJohn Baldwin nvmf_discovery_portal::handle_connection(freebsd::fd_up fd,
48066b5296fSJohn Baldwin const char *host __unused, const struct sockaddr *client_sa)
48166b5296fSJohn Baldwin {
48266b5296fSJohn Baldwin struct nvmf_qpair_params qparams;
48366b5296fSJohn Baldwin memset(&qparams, 0, sizeof(qparams));
48466b5296fSJohn Baldwin qparams.tcp.fd = fd;
48566b5296fSJohn Baldwin
48666b5296fSJohn Baldwin struct nvmf_capsule *nc = NULL;
48766b5296fSJohn Baldwin struct nvmf_fabric_connect_data data;
48866b5296fSJohn Baldwin nvmf_qpair_up qp(nvmf_accept(association(), &qparams, &nc, &data));
48966b5296fSJohn Baldwin if (!qp) {
49066b5296fSJohn Baldwin log_warnx("Failed to create NVMe discovery qpair: %s",
49166b5296fSJohn Baldwin nvmf_association_error(association()));
49266b5296fSJohn Baldwin return;
49366b5296fSJohn Baldwin }
49466b5296fSJohn Baldwin nvmf_capsule_up nc_guard(nc);
49566b5296fSJohn Baldwin
49666b5296fSJohn Baldwin if (strncmp((char *)data.subnqn, NVMF_DISCOVERY_NQN,
49766b5296fSJohn Baldwin sizeof(data.subnqn)) != 0) {
49866b5296fSJohn Baldwin log_warnx("Discovery NVMe qpair with invalid SubNQN: %.*s",
49966b5296fSJohn Baldwin (int)sizeof(data.subnqn), data.subnqn);
50066b5296fSJohn Baldwin nvmf_connect_invalid_parameters(nc, true,
50166b5296fSJohn Baldwin offsetof(struct nvmf_fabric_connect_data, subnqn));
50266b5296fSJohn Baldwin return;
50366b5296fSJohn Baldwin }
50466b5296fSJohn Baldwin
50566b5296fSJohn Baldwin /* Just use a controller ID of 1 for all discovery controllers. */
50666b5296fSJohn Baldwin int error = nvmf_finish_accept(nc, 1);
50766b5296fSJohn Baldwin if (error != 0) {
50866b5296fSJohn Baldwin log_warnc(error, "Failed to send NVMe CONNECT reponse");
50966b5296fSJohn Baldwin return;
51066b5296fSJohn Baldwin }
51166b5296fSJohn Baldwin nc_guard.reset();
51266b5296fSJohn Baldwin
51366b5296fSJohn Baldwin discovery_log discovery_log = build_discovery_log_page(portal_group(),
51466b5296fSJohn Baldwin fd, client_sa, data);
51566b5296fSJohn Baldwin
51666b5296fSJohn Baldwin discovery_controller controller(std::move(fd), qp.get(), discovery_log);
51766b5296fSJohn Baldwin controller.handle_admin_commands();
51866b5296fSJohn Baldwin }
519