xref: /freebsd/usr.sbin/ctld/nvmf_discovery.cc (revision d21b513988b7d685edf2ad77886b3e3c95cc0df8)
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