xref: /freebsd/usr.sbin/nvmfd/discovery.c (revision 399362bac312d4fa77a3fd918ea002c0782bc315)
1a8089ea5SJohn Baldwin /*-
2a8089ea5SJohn Baldwin  * SPDX-License-Identifier: BSD-2-Clause
3a8089ea5SJohn Baldwin  *
4a8089ea5SJohn Baldwin  * Copyright (c) 2023-2024 Chelsio Communications, Inc.
5a8089ea5SJohn Baldwin  * Written by: John Baldwin <jhb@FreeBSD.org>
6a8089ea5SJohn Baldwin  */
7a8089ea5SJohn Baldwin 
8a8089ea5SJohn Baldwin #include <sys/socket.h>
9a8089ea5SJohn Baldwin #include <netinet/in.h>
10a8089ea5SJohn Baldwin #include <arpa/inet.h>
11a8089ea5SJohn Baldwin #include <assert.h>
12a8089ea5SJohn Baldwin #include <err.h>
13a8089ea5SJohn Baldwin #include <libnvmf.h>
14a8089ea5SJohn Baldwin #include <pthread.h>
15a8089ea5SJohn Baldwin #include <stdio.h>
16a8089ea5SJohn Baldwin #include <stdlib.h>
17a8089ea5SJohn Baldwin #include <string.h>
18a8089ea5SJohn Baldwin #include <unistd.h>
19a8089ea5SJohn Baldwin 
20a8089ea5SJohn Baldwin #include "internal.h"
21a8089ea5SJohn Baldwin 
22a8089ea5SJohn Baldwin struct io_controller_data {
23a8089ea5SJohn Baldwin 	struct nvme_discovery_log_entry entry;
24a8089ea5SJohn Baldwin 	bool wildcard;
25a8089ea5SJohn Baldwin };
26a8089ea5SJohn Baldwin 
27a8089ea5SJohn Baldwin struct discovery_controller {
28a8089ea5SJohn Baldwin 	struct nvme_discovery_log *discovery_log;
29a8089ea5SJohn Baldwin 	size_t discovery_log_len;
30a8089ea5SJohn Baldwin 	int s;
31a8089ea5SJohn Baldwin };
32a8089ea5SJohn Baldwin 
33a8089ea5SJohn Baldwin struct discovery_thread_arg {
34a8089ea5SJohn Baldwin 	struct controller *c;
35a8089ea5SJohn Baldwin 	struct nvmf_qpair *qp;
36a8089ea5SJohn Baldwin 	int s;
37a8089ea5SJohn Baldwin };
38a8089ea5SJohn Baldwin 
39a8089ea5SJohn Baldwin static struct io_controller_data *io_controllers;
40a8089ea5SJohn Baldwin static struct nvmf_association *discovery_na;
41a8089ea5SJohn Baldwin static u_int num_io_controllers;
42a8089ea5SJohn Baldwin 
43a8089ea5SJohn Baldwin static bool
44a8089ea5SJohn Baldwin init_discovery_log_entry(struct nvme_discovery_log_entry *entry, int s,
45a8089ea5SJohn Baldwin     const char *subnqn)
46a8089ea5SJohn Baldwin {
47a8089ea5SJohn Baldwin 	struct sockaddr_storage ss;
48a8089ea5SJohn Baldwin 	socklen_t len;
49a8089ea5SJohn Baldwin 	bool wildcard;
50a8089ea5SJohn Baldwin 
51a8089ea5SJohn Baldwin 	len = sizeof(ss);
52a8089ea5SJohn Baldwin 	if (getsockname(s, (struct sockaddr *)&ss, &len) == -1)
53a8089ea5SJohn Baldwin 		err(1, "getsockname");
54a8089ea5SJohn Baldwin 
55a8089ea5SJohn Baldwin 	memset(entry, 0, sizeof(*entry));
56a8089ea5SJohn Baldwin 	entry->trtype = NVMF_TRTYPE_TCP;
57a8089ea5SJohn Baldwin 	switch (ss.ss_family) {
58a8089ea5SJohn Baldwin 	case AF_INET:
59a8089ea5SJohn Baldwin 	{
60a8089ea5SJohn Baldwin 		struct sockaddr_in *sin;
61a8089ea5SJohn Baldwin 
62a8089ea5SJohn Baldwin 		sin = (struct sockaddr_in *)&ss;
63a8089ea5SJohn Baldwin 		entry->adrfam = NVMF_ADRFAM_IPV4;
64a8089ea5SJohn Baldwin 		snprintf(entry->trsvcid, sizeof(entry->trsvcid), "%u",
65a8089ea5SJohn Baldwin 		    htons(sin->sin_port));
66a8089ea5SJohn Baldwin 		if (inet_ntop(AF_INET, &sin->sin_addr, entry->traddr,
67a8089ea5SJohn Baldwin 		    sizeof(entry->traddr)) == NULL)
68a8089ea5SJohn Baldwin 			err(1, "inet_ntop");
69a8089ea5SJohn Baldwin 		wildcard = (sin->sin_addr.s_addr == htonl(INADDR_ANY));
70a8089ea5SJohn Baldwin 		break;
71a8089ea5SJohn Baldwin 	}
72a8089ea5SJohn Baldwin 	case AF_INET6:
73a8089ea5SJohn Baldwin 	{
74a8089ea5SJohn Baldwin 		struct sockaddr_in6 *sin6;
75a8089ea5SJohn Baldwin 
76a8089ea5SJohn Baldwin 		sin6 = (struct sockaddr_in6 *)&ss;
77a8089ea5SJohn Baldwin 		entry->adrfam = NVMF_ADRFAM_IPV6;
78a8089ea5SJohn Baldwin 		snprintf(entry->trsvcid, sizeof(entry->trsvcid), "%u",
79a8089ea5SJohn Baldwin 		    htons(sin6->sin6_port));
80a8089ea5SJohn Baldwin 		if (inet_ntop(AF_INET6, &sin6->sin6_addr, entry->traddr,
81a8089ea5SJohn Baldwin 		    sizeof(entry->traddr)) == NULL)
82a8089ea5SJohn Baldwin 			err(1, "inet_ntop");
83a8089ea5SJohn Baldwin 		wildcard = (memcmp(&sin6->sin6_addr, &in6addr_any,
84a8089ea5SJohn Baldwin 		    sizeof(in6addr_any)) == 0);
85a8089ea5SJohn Baldwin 		break;
86a8089ea5SJohn Baldwin 	}
87a8089ea5SJohn Baldwin 	default:
88a8089ea5SJohn Baldwin 		errx(1, "Unsupported address family %u", ss.ss_family);
89a8089ea5SJohn Baldwin 	}
90a8089ea5SJohn Baldwin 	entry->subtype = NVMF_SUBTYPE_NVME;
91a8089ea5SJohn Baldwin 	if (flow_control_disable)
92a8089ea5SJohn Baldwin 		entry->treq |= (1 << 2);
93a8089ea5SJohn Baldwin 	entry->portid = htole16(1);
94a8089ea5SJohn Baldwin 	entry->cntlid = htole16(NVMF_CNTLID_DYNAMIC);
95a8089ea5SJohn Baldwin 	entry->aqsz = NVME_MAX_ADMIN_ENTRIES;
96a8089ea5SJohn Baldwin 	strlcpy(entry->subnqn, subnqn, sizeof(entry->subnqn));
97a8089ea5SJohn Baldwin 	return (wildcard);
98a8089ea5SJohn Baldwin }
99a8089ea5SJohn Baldwin 
100a8089ea5SJohn Baldwin void
101a8089ea5SJohn Baldwin init_discovery(void)
102a8089ea5SJohn Baldwin {
103a8089ea5SJohn Baldwin 	struct nvmf_association_params aparams;
104a8089ea5SJohn Baldwin 
105a8089ea5SJohn Baldwin 	memset(&aparams, 0, sizeof(aparams));
106a8089ea5SJohn Baldwin 	aparams.sq_flow_control = false;
107a8089ea5SJohn Baldwin 	aparams.dynamic_controller_model = true;
108a8089ea5SJohn Baldwin 	aparams.max_admin_qsize = NVME_MAX_ADMIN_ENTRIES;
109a8089ea5SJohn Baldwin 	aparams.tcp.pda = 0;
110a8089ea5SJohn Baldwin 	aparams.tcp.header_digests = header_digests;
111a8089ea5SJohn Baldwin 	aparams.tcp.data_digests = data_digests;
112*399362baSJohn Baldwin 	aparams.tcp.maxh2cdata = maxh2cdata;
113a8089ea5SJohn Baldwin 	discovery_na = nvmf_allocate_association(NVMF_TRTYPE_TCP, true,
114a8089ea5SJohn Baldwin 	    &aparams);
115a8089ea5SJohn Baldwin 	if (discovery_na == NULL)
116a8089ea5SJohn Baldwin 		err(1, "Failed to create discovery association");
117a8089ea5SJohn Baldwin }
118a8089ea5SJohn Baldwin 
119a8089ea5SJohn Baldwin void
120a8089ea5SJohn Baldwin discovery_add_io_controller(int s, const char *subnqn)
121a8089ea5SJohn Baldwin {
122a8089ea5SJohn Baldwin 	struct io_controller_data *icd;
123a8089ea5SJohn Baldwin 
124a8089ea5SJohn Baldwin 	io_controllers = reallocf(io_controllers, (num_io_controllers + 1) *
125a8089ea5SJohn Baldwin 	    sizeof(*io_controllers));
126a8089ea5SJohn Baldwin 
127a8089ea5SJohn Baldwin 	icd = &io_controllers[num_io_controllers];
128a8089ea5SJohn Baldwin 	num_io_controllers++;
129a8089ea5SJohn Baldwin 
130a8089ea5SJohn Baldwin 	icd->wildcard = init_discovery_log_entry(&icd->entry, s, subnqn);
131a8089ea5SJohn Baldwin }
132a8089ea5SJohn Baldwin 
133a8089ea5SJohn Baldwin static void
134a8089ea5SJohn Baldwin build_discovery_log_page(struct discovery_controller *dc)
135a8089ea5SJohn Baldwin {
136a8089ea5SJohn Baldwin 	struct sockaddr_storage ss;
137a8089ea5SJohn Baldwin 	socklen_t len;
138a8089ea5SJohn Baldwin 	char traddr[256];
139a8089ea5SJohn Baldwin 	u_int i, nentries;
140a8089ea5SJohn Baldwin 	uint8_t adrfam;
141a8089ea5SJohn Baldwin 
142a8089ea5SJohn Baldwin 	if (dc->discovery_log != NULL)
143a8089ea5SJohn Baldwin 		return;
144a8089ea5SJohn Baldwin 
145a8089ea5SJohn Baldwin 	len = sizeof(ss);
146a8089ea5SJohn Baldwin 	if (getsockname(dc->s, (struct sockaddr *)&ss, &len) == -1) {
147a8089ea5SJohn Baldwin 		warn("build_discovery_log_page: getsockname");
148a8089ea5SJohn Baldwin 		return;
149a8089ea5SJohn Baldwin 	}
150a8089ea5SJohn Baldwin 
151a8089ea5SJohn Baldwin 	memset(traddr, 0, sizeof(traddr));
152a8089ea5SJohn Baldwin 	switch (ss.ss_family) {
153a8089ea5SJohn Baldwin 	case AF_INET:
154a8089ea5SJohn Baldwin 	{
155a8089ea5SJohn Baldwin 		struct sockaddr_in *sin;
156a8089ea5SJohn Baldwin 
157a8089ea5SJohn Baldwin 		sin = (struct sockaddr_in *)&ss;
158a8089ea5SJohn Baldwin 		adrfam = NVMF_ADRFAM_IPV4;
159a8089ea5SJohn Baldwin 		if (inet_ntop(AF_INET, &sin->sin_addr, traddr,
160a8089ea5SJohn Baldwin 		    sizeof(traddr)) == NULL) {
161a8089ea5SJohn Baldwin 			warn("build_discovery_log_page: inet_ntop");
162a8089ea5SJohn Baldwin 			return;
163a8089ea5SJohn Baldwin 		}
164a8089ea5SJohn Baldwin 		break;
165a8089ea5SJohn Baldwin 	}
166a8089ea5SJohn Baldwin 	case AF_INET6:
167a8089ea5SJohn Baldwin 	{
168a8089ea5SJohn Baldwin 		struct sockaddr_in6 *sin6;
169a8089ea5SJohn Baldwin 
170a8089ea5SJohn Baldwin 		sin6 = (struct sockaddr_in6 *)&ss;
171a8089ea5SJohn Baldwin 		adrfam = NVMF_ADRFAM_IPV6;
172a8089ea5SJohn Baldwin 		if (inet_ntop(AF_INET6, &sin6->sin6_addr, traddr,
173a8089ea5SJohn Baldwin 		    sizeof(traddr)) == NULL) {
174a8089ea5SJohn Baldwin 			warn("build_discovery_log_page: inet_ntop");
175a8089ea5SJohn Baldwin 			return;
176a8089ea5SJohn Baldwin 		}
177a8089ea5SJohn Baldwin 		break;
178a8089ea5SJohn Baldwin 	}
179a8089ea5SJohn Baldwin 	default:
180a8089ea5SJohn Baldwin 		assert(false);
181a8089ea5SJohn Baldwin 	}
182a8089ea5SJohn Baldwin 
183a8089ea5SJohn Baldwin 	nentries = 0;
184a8089ea5SJohn Baldwin 	for (i = 0; i < num_io_controllers; i++) {
185a8089ea5SJohn Baldwin 		if (io_controllers[i].wildcard &&
186a8089ea5SJohn Baldwin 		    io_controllers[i].entry.adrfam != adrfam)
187a8089ea5SJohn Baldwin 			continue;
188a8089ea5SJohn Baldwin 		nentries++;
189a8089ea5SJohn Baldwin 	}
190a8089ea5SJohn Baldwin 
191a8089ea5SJohn Baldwin 	dc->discovery_log_len = sizeof(*dc->discovery_log) +
192a8089ea5SJohn Baldwin 	    nentries * sizeof(struct nvme_discovery_log_entry);
193a8089ea5SJohn Baldwin 	dc->discovery_log = calloc(dc->discovery_log_len, 1);
194a8089ea5SJohn Baldwin 	dc->discovery_log->numrec = nentries;
195a8089ea5SJohn Baldwin 	dc->discovery_log->recfmt = 0;
196a8089ea5SJohn Baldwin 	nentries = 0;
197a8089ea5SJohn Baldwin 	for (i = 0; i < num_io_controllers; i++) {
198a8089ea5SJohn Baldwin 		if (io_controllers[i].wildcard &&
199a8089ea5SJohn Baldwin 		    io_controllers[i].entry.adrfam != adrfam)
200a8089ea5SJohn Baldwin 			continue;
201a8089ea5SJohn Baldwin 
202a8089ea5SJohn Baldwin 		dc->discovery_log->entries[nentries] = io_controllers[i].entry;
203a8089ea5SJohn Baldwin 		if (io_controllers[i].wildcard)
204a8089ea5SJohn Baldwin 			memcpy(dc->discovery_log->entries[nentries].traddr,
205a8089ea5SJohn Baldwin 			    traddr, sizeof(traddr));
206a8089ea5SJohn Baldwin 	}
207a8089ea5SJohn Baldwin }
208a8089ea5SJohn Baldwin 
209a8089ea5SJohn Baldwin static void
210a8089ea5SJohn Baldwin handle_get_log_page_command(const struct nvmf_capsule *nc,
211a8089ea5SJohn Baldwin     const struct nvme_command *cmd, struct discovery_controller *dc)
212a8089ea5SJohn Baldwin {
213a8089ea5SJohn Baldwin 	uint64_t offset;
214a8089ea5SJohn Baldwin 	uint32_t length;
215a8089ea5SJohn Baldwin 
216a8089ea5SJohn Baldwin 	switch (nvmf_get_log_page_id(cmd)) {
217a8089ea5SJohn Baldwin 	case NVME_LOG_DISCOVERY:
218a8089ea5SJohn Baldwin 		break;
219a8089ea5SJohn Baldwin 	default:
220a8089ea5SJohn Baldwin 		warnx("Unsupported log page %u for discovery controller",
221a8089ea5SJohn Baldwin 		    nvmf_get_log_page_id(cmd));
222a8089ea5SJohn Baldwin 		goto error;
223a8089ea5SJohn Baldwin 	}
224a8089ea5SJohn Baldwin 
225a8089ea5SJohn Baldwin 	build_discovery_log_page(dc);
226a8089ea5SJohn Baldwin 
227a8089ea5SJohn Baldwin 	offset = nvmf_get_log_page_offset(cmd);
228a8089ea5SJohn Baldwin 	if (offset >= dc->discovery_log_len)
229a8089ea5SJohn Baldwin 		goto error;
230a8089ea5SJohn Baldwin 
231a8089ea5SJohn Baldwin 	length = nvmf_get_log_page_length(cmd);
232a8089ea5SJohn Baldwin 	if (length > dc->discovery_log_len - offset)
233a8089ea5SJohn Baldwin 		length = dc->discovery_log_len - offset;
234a8089ea5SJohn Baldwin 
235a8089ea5SJohn Baldwin 	nvmf_send_controller_data(nc, (char *)dc->discovery_log + offset,
236a8089ea5SJohn Baldwin 	    length);
237a8089ea5SJohn Baldwin 	return;
238a8089ea5SJohn Baldwin error:
239a8089ea5SJohn Baldwin 	nvmf_send_generic_error(nc, NVME_SC_INVALID_FIELD);
240a8089ea5SJohn Baldwin }
241a8089ea5SJohn Baldwin 
242a8089ea5SJohn Baldwin static bool
243a8089ea5SJohn Baldwin discovery_command(const struct nvmf_capsule *nc, const struct nvme_command *cmd,
244a8089ea5SJohn Baldwin     void *arg)
245a8089ea5SJohn Baldwin {
246a8089ea5SJohn Baldwin 	struct discovery_controller *dc = arg;
247a8089ea5SJohn Baldwin 
248a8089ea5SJohn Baldwin 	switch (cmd->opc) {
249a8089ea5SJohn Baldwin 	case NVME_OPC_GET_LOG_PAGE:
250a8089ea5SJohn Baldwin 		handle_get_log_page_command(nc, cmd, dc);
251a8089ea5SJohn Baldwin 		return (true);
252a8089ea5SJohn Baldwin 	default:
253a8089ea5SJohn Baldwin 		return (false);
254a8089ea5SJohn Baldwin 	}
255a8089ea5SJohn Baldwin }
256a8089ea5SJohn Baldwin 
257a8089ea5SJohn Baldwin static void *
258a8089ea5SJohn Baldwin discovery_thread(void *arg)
259a8089ea5SJohn Baldwin {
260a8089ea5SJohn Baldwin 	struct discovery_thread_arg *dta = arg;
261a8089ea5SJohn Baldwin 	struct discovery_controller dc;
262a8089ea5SJohn Baldwin 
263a8089ea5SJohn Baldwin 	pthread_detach(pthread_self());
264a8089ea5SJohn Baldwin 
265a8089ea5SJohn Baldwin 	memset(&dc, 0, sizeof(dc));
266a8089ea5SJohn Baldwin 	dc.s = dta->s;
267a8089ea5SJohn Baldwin 
268a8089ea5SJohn Baldwin 	controller_handle_admin_commands(dta->c, discovery_command, &dc);
269a8089ea5SJohn Baldwin 
270a8089ea5SJohn Baldwin 	free(dc.discovery_log);
271a8089ea5SJohn Baldwin 	free_controller(dta->c);
272a8089ea5SJohn Baldwin 
273a8089ea5SJohn Baldwin 	nvmf_free_qpair(dta->qp);
274a8089ea5SJohn Baldwin 
275a8089ea5SJohn Baldwin 	close(dta->s);
276a8089ea5SJohn Baldwin 	free(dta);
277a8089ea5SJohn Baldwin 	return (NULL);
278a8089ea5SJohn Baldwin }
279a8089ea5SJohn Baldwin 
280a8089ea5SJohn Baldwin void
281a8089ea5SJohn Baldwin handle_discovery_socket(int s)
282a8089ea5SJohn Baldwin {
283a8089ea5SJohn Baldwin 	struct nvmf_fabric_connect_data data;
284a8089ea5SJohn Baldwin 	struct nvme_controller_data cdata;
285a8089ea5SJohn Baldwin 	struct nvmf_qpair_params qparams;
286a8089ea5SJohn Baldwin 	struct discovery_thread_arg *dta;
287a8089ea5SJohn Baldwin 	struct nvmf_capsule *nc;
288a8089ea5SJohn Baldwin 	struct nvmf_qpair *qp;
289a8089ea5SJohn Baldwin 	pthread_t thr;
290a8089ea5SJohn Baldwin 	int error;
291a8089ea5SJohn Baldwin 
292a8089ea5SJohn Baldwin 	memset(&qparams, 0, sizeof(qparams));
293a8089ea5SJohn Baldwin 	qparams.tcp.fd = s;
294a8089ea5SJohn Baldwin 
295a8089ea5SJohn Baldwin 	nc = NULL;
296a8089ea5SJohn Baldwin 	qp = nvmf_accept(discovery_na, &qparams, &nc, &data);
297a8089ea5SJohn Baldwin 	if (qp == NULL) {
298a8089ea5SJohn Baldwin 		warnx("Failed to create discovery qpair: %s",
299a8089ea5SJohn Baldwin 		    nvmf_association_error(discovery_na));
300a8089ea5SJohn Baldwin 		goto error;
301a8089ea5SJohn Baldwin 	}
302a8089ea5SJohn Baldwin 
303a8089ea5SJohn Baldwin 	if (strcmp(data.subnqn, NVMF_DISCOVERY_NQN) != 0) {
304a8089ea5SJohn Baldwin 		warn("Discovery qpair with invalid SubNQN: %.*s",
305a8089ea5SJohn Baldwin 		    (int)sizeof(data.subnqn), data.subnqn);
306a8089ea5SJohn Baldwin 		nvmf_connect_invalid_parameters(nc, true,
307a8089ea5SJohn Baldwin 		    offsetof(struct nvmf_fabric_connect_data, subnqn));
308a8089ea5SJohn Baldwin 		goto error;
309a8089ea5SJohn Baldwin 	}
310a8089ea5SJohn Baldwin 
311a8089ea5SJohn Baldwin 	/* Just use a controller ID of 1 for all discovery controllers. */
312a8089ea5SJohn Baldwin 	error = nvmf_finish_accept(nc, 1);
313a8089ea5SJohn Baldwin 	if (error != 0) {
314a8089ea5SJohn Baldwin 		warnc(error, "Failed to send CONNECT reponse");
315a8089ea5SJohn Baldwin 		goto error;
316a8089ea5SJohn Baldwin 	}
317a8089ea5SJohn Baldwin 
318a8089ea5SJohn Baldwin 	nvmf_init_discovery_controller_data(qp, &cdata);
319a8089ea5SJohn Baldwin 
320a8089ea5SJohn Baldwin 	dta = malloc(sizeof(*dta));
321a8089ea5SJohn Baldwin 	dta->qp = qp;
322a8089ea5SJohn Baldwin 	dta->s = s;
323a8089ea5SJohn Baldwin 	dta->c = init_controller(qp, &cdata);
324a8089ea5SJohn Baldwin 
325a8089ea5SJohn Baldwin 	error = pthread_create(&thr, NULL, discovery_thread, dta);
326a8089ea5SJohn Baldwin 	if (error != 0) {
327a8089ea5SJohn Baldwin 		warnc(error, "Failed to create discovery thread");
328a8089ea5SJohn Baldwin 		free_controller(dta->c);
329a8089ea5SJohn Baldwin 		free(dta);
330a8089ea5SJohn Baldwin 		goto error;
331a8089ea5SJohn Baldwin 	}
332a8089ea5SJohn Baldwin 
333a8089ea5SJohn Baldwin 	nvmf_free_capsule(nc);
334a8089ea5SJohn Baldwin 	return;
335a8089ea5SJohn Baldwin 
336a8089ea5SJohn Baldwin error:
337a8089ea5SJohn Baldwin 	if (nc != NULL)
338a8089ea5SJohn Baldwin 		nvmf_free_capsule(nc);
339a8089ea5SJohn Baldwin 	if (qp != NULL)
340a8089ea5SJohn Baldwin 		nvmf_free_qpair(qp);
341a8089ea5SJohn Baldwin 	close(s);
342a8089ea5SJohn Baldwin }
343