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