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