xref: /freebsd/usr.sbin/nvmfd/discovery.c (revision a7148ab39c03abd4d1a84997c70bf96f15dd2a09)
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.maxh2cdata = maxh2cdata;
113 	discovery_na = nvmf_allocate_association(NVMF_TRTYPE_TCP, true,
114 	    &aparams);
115 	if (discovery_na == NULL)
116 		err(1, "Failed to create discovery association");
117 }
118 
119 void
120 discovery_add_io_controller(int s, const char *subnqn)
121 {
122 	struct io_controller_data *icd;
123 
124 	io_controllers = reallocf(io_controllers, (num_io_controllers + 1) *
125 	    sizeof(*io_controllers));
126 
127 	icd = &io_controllers[num_io_controllers];
128 	num_io_controllers++;
129 
130 	icd->wildcard = init_discovery_log_entry(&icd->entry, s, subnqn);
131 }
132 
133 static void
134 build_discovery_log_page(struct discovery_controller *dc)
135 {
136 	struct sockaddr_storage ss;
137 	socklen_t len;
138 	char traddr[256];
139 	u_int i, nentries;
140 	uint8_t adrfam;
141 
142 	if (dc->discovery_log != NULL)
143 		return;
144 
145 	len = sizeof(ss);
146 	if (getsockname(dc->s, (struct sockaddr *)&ss, &len) == -1) {
147 		warn("build_discovery_log_page: getsockname");
148 		return;
149 	}
150 
151 	memset(traddr, 0, sizeof(traddr));
152 	switch (ss.ss_family) {
153 	case AF_INET:
154 	{
155 		struct sockaddr_in *sin;
156 
157 		sin = (struct sockaddr_in *)&ss;
158 		adrfam = NVMF_ADRFAM_IPV4;
159 		if (inet_ntop(AF_INET, &sin->sin_addr, traddr,
160 		    sizeof(traddr)) == NULL) {
161 			warn("build_discovery_log_page: inet_ntop");
162 			return;
163 		}
164 		break;
165 	}
166 	case AF_INET6:
167 	{
168 		struct sockaddr_in6 *sin6;
169 
170 		sin6 = (struct sockaddr_in6 *)&ss;
171 		adrfam = NVMF_ADRFAM_IPV6;
172 		if (inet_ntop(AF_INET6, &sin6->sin6_addr, traddr,
173 		    sizeof(traddr)) == NULL) {
174 			warn("build_discovery_log_page: inet_ntop");
175 			return;
176 		}
177 		break;
178 	}
179 	default:
180 		assert(false);
181 	}
182 
183 	nentries = 0;
184 	for (i = 0; i < num_io_controllers; i++) {
185 		if (io_controllers[i].wildcard &&
186 		    io_controllers[i].entry.adrfam != adrfam)
187 			continue;
188 		nentries++;
189 	}
190 
191 	dc->discovery_log_len = sizeof(*dc->discovery_log) +
192 	    nentries * sizeof(struct nvme_discovery_log_entry);
193 	dc->discovery_log = calloc(dc->discovery_log_len, 1);
194 	dc->discovery_log->numrec = nentries;
195 	dc->discovery_log->recfmt = 0;
196 	nentries = 0;
197 	for (i = 0; i < num_io_controllers; i++) {
198 		if (io_controllers[i].wildcard &&
199 		    io_controllers[i].entry.adrfam != adrfam)
200 			continue;
201 
202 		dc->discovery_log->entries[nentries] = io_controllers[i].entry;
203 		if (io_controllers[i].wildcard)
204 			memcpy(dc->discovery_log->entries[nentries].traddr,
205 			    traddr, sizeof(traddr));
206 	}
207 }
208 
209 static void
210 handle_get_log_page_command(const struct nvmf_capsule *nc,
211     const struct nvme_command *cmd, struct discovery_controller *dc)
212 {
213 	uint64_t offset;
214 	uint32_t length;
215 
216 	switch (nvmf_get_log_page_id(cmd)) {
217 	case NVME_LOG_DISCOVERY:
218 		break;
219 	default:
220 		warnx("Unsupported log page %u for discovery controller",
221 		    nvmf_get_log_page_id(cmd));
222 		goto error;
223 	}
224 
225 	build_discovery_log_page(dc);
226 
227 	offset = nvmf_get_log_page_offset(cmd);
228 	if (offset >= dc->discovery_log_len)
229 		goto error;
230 
231 	length = nvmf_get_log_page_length(cmd);
232 	if (length > dc->discovery_log_len - offset)
233 		length = dc->discovery_log_len - offset;
234 
235 	nvmf_send_controller_data(nc, (char *)dc->discovery_log + offset,
236 	    length);
237 	return;
238 error:
239 	nvmf_send_generic_error(nc, NVME_SC_INVALID_FIELD);
240 }
241 
242 static bool
243 discovery_command(const struct nvmf_capsule *nc, const struct nvme_command *cmd,
244     void *arg)
245 {
246 	struct discovery_controller *dc = arg;
247 
248 	switch (cmd->opc) {
249 	case NVME_OPC_GET_LOG_PAGE:
250 		handle_get_log_page_command(nc, cmd, dc);
251 		return (true);
252 	default:
253 		return (false);
254 	}
255 }
256 
257 static void *
258 discovery_thread(void *arg)
259 {
260 	struct discovery_thread_arg *dta = arg;
261 	struct discovery_controller dc;
262 
263 	pthread_detach(pthread_self());
264 
265 	memset(&dc, 0, sizeof(dc));
266 	dc.s = dta->s;
267 
268 	controller_handle_admin_commands(dta->c, discovery_command, &dc);
269 
270 	free(dc.discovery_log);
271 	free_controller(dta->c);
272 
273 	nvmf_free_qpair(dta->qp);
274 
275 	close(dta->s);
276 	free(dta);
277 	return (NULL);
278 }
279 
280 void
281 handle_discovery_socket(int s)
282 {
283 	struct nvmf_fabric_connect_data data;
284 	struct nvme_controller_data cdata;
285 	struct nvmf_qpair_params qparams;
286 	struct discovery_thread_arg *dta;
287 	struct nvmf_capsule *nc;
288 	struct nvmf_qpair *qp;
289 	pthread_t thr;
290 	int error;
291 
292 	memset(&qparams, 0, sizeof(qparams));
293 	qparams.tcp.fd = s;
294 
295 	nc = NULL;
296 	qp = nvmf_accept(discovery_na, &qparams, &nc, &data);
297 	if (qp == NULL) {
298 		warnx("Failed to create discovery qpair: %s",
299 		    nvmf_association_error(discovery_na));
300 		goto error;
301 	}
302 
303 	if (strcmp(data.subnqn, NVMF_DISCOVERY_NQN) != 0) {
304 		warn("Discovery qpair with invalid SubNQN: %.*s",
305 		    (int)sizeof(data.subnqn), data.subnqn);
306 		nvmf_connect_invalid_parameters(nc, true,
307 		    offsetof(struct nvmf_fabric_connect_data, subnqn));
308 		goto error;
309 	}
310 
311 	/* Just use a controller ID of 1 for all discovery controllers. */
312 	error = nvmf_finish_accept(nc, 1);
313 	if (error != 0) {
314 		warnc(error, "Failed to send CONNECT reponse");
315 		goto error;
316 	}
317 
318 	nvmf_init_discovery_controller_data(qp, &cdata);
319 
320 	dta = malloc(sizeof(*dta));
321 	dta->qp = qp;
322 	dta->s = s;
323 	dta->c = init_controller(qp, &cdata);
324 
325 	error = pthread_create(&thr, NULL, discovery_thread, dta);
326 	if (error != 0) {
327 		warnc(error, "Failed to create discovery thread");
328 		free_controller(dta->c);
329 		free(dta);
330 		goto error;
331 	}
332 
333 	nvmf_free_capsule(nc);
334 	return;
335 
336 error:
337 	if (nc != NULL)
338 		nvmf_free_capsule(nc);
339 	if (qp != NULL)
340 		nvmf_free_qpair(qp);
341 	close(s);
342 }
343