xref: /freebsd/usr.sbin/nvmfd/nvmfd.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/param.h>
9a8089ea5SJohn Baldwin #include <sys/event.h>
10a8089ea5SJohn Baldwin #include <sys/linker.h>
11a8089ea5SJohn Baldwin #include <sys/module.h>
12a8089ea5SJohn Baldwin #include <sys/socket.h>
13a8089ea5SJohn Baldwin #include <netinet/in.h>
14a8089ea5SJohn Baldwin #include <assert.h>
15a8089ea5SJohn Baldwin #include <err.h>
16a8089ea5SJohn Baldwin #include <errno.h>
17a8089ea5SJohn Baldwin #include <libnvmf.h>
18a8089ea5SJohn Baldwin #include <libutil.h>
19a8089ea5SJohn Baldwin #include <netdb.h>
20a8089ea5SJohn Baldwin #include <signal.h>
21*399362baSJohn Baldwin #include <stdint.h>
22a8089ea5SJohn Baldwin #include <stdio.h>
23a8089ea5SJohn Baldwin #include <stdlib.h>
24a8089ea5SJohn Baldwin #include <string.h>
25a8089ea5SJohn Baldwin #include <unistd.h>
26a8089ea5SJohn Baldwin 
27a8089ea5SJohn Baldwin #include "internal.h"
28a8089ea5SJohn Baldwin 
29a8089ea5SJohn Baldwin bool data_digests = false;
30a8089ea5SJohn Baldwin bool header_digests = false;
31a8089ea5SJohn Baldwin bool flow_control_disable = false;
32a8089ea5SJohn Baldwin bool kernel_io = false;
33*399362baSJohn Baldwin uint32_t maxh2cdata = 256 * 1024;
34a8089ea5SJohn Baldwin 
35a8089ea5SJohn Baldwin static const char *subnqn;
36a8089ea5SJohn Baldwin static volatile bool quit = false;
37a8089ea5SJohn Baldwin 
38a8089ea5SJohn Baldwin static void
39a8089ea5SJohn Baldwin usage(void)
40a8089ea5SJohn Baldwin {
41*399362baSJohn Baldwin 	fprintf(stderr, "nvmfd -K [-dFGg] [-H MAXH2CDATA] [-P port] [-p port] [-t transport] [-n subnqn]\n"
42*399362baSJohn Baldwin 	    "nvmfd [-dFGg] [-H MAXH2CDATA] [-P port] [-p port] [-t transport] [-n subnqn]\n"
43a8089ea5SJohn Baldwin 	    "\tdevice [device [...]]\n"
44a8089ea5SJohn Baldwin 	    "\n"
45a8089ea5SJohn Baldwin 	    "Devices use one of the following syntaxes:\n"
46a8089ea5SJohn Baldwin 	    "\tpathame      - file or disk device\n"
47a8089ea5SJohn Baldwin 	    "\tramdisk:size - memory disk of given size\n");
48a8089ea5SJohn Baldwin 	exit(1);
49a8089ea5SJohn Baldwin }
50a8089ea5SJohn Baldwin 
51a8089ea5SJohn Baldwin static void
52a8089ea5SJohn Baldwin handle_sig(int sig __unused)
53a8089ea5SJohn Baldwin {
54a8089ea5SJohn Baldwin 	quit = true;
55a8089ea5SJohn Baldwin }
56a8089ea5SJohn Baldwin 
57a8089ea5SJohn Baldwin static void
58a8089ea5SJohn Baldwin register_listen_socket(int kqfd, int s, void *udata)
59a8089ea5SJohn Baldwin {
60a8089ea5SJohn Baldwin 	struct kevent kev;
61a8089ea5SJohn Baldwin 
62a8089ea5SJohn Baldwin 	if (listen(s, -1) != 0)
63a8089ea5SJohn Baldwin 		err(1, "listen");
64a8089ea5SJohn Baldwin 
65a8089ea5SJohn Baldwin 	EV_SET(&kev, s, EVFILT_READ, EV_ADD, 0, 0, udata);
66a8089ea5SJohn Baldwin 	if (kevent(kqfd, &kev, 1, NULL, 0, NULL) == -1)
67a8089ea5SJohn Baldwin 		err(1, "kevent: failed to add listen socket");
68a8089ea5SJohn Baldwin }
69a8089ea5SJohn Baldwin 
70a8089ea5SJohn Baldwin static void
71a8089ea5SJohn Baldwin create_passive_sockets(int kqfd, const char *port, bool discovery)
72a8089ea5SJohn Baldwin {
73a8089ea5SJohn Baldwin 	struct addrinfo hints, *ai, *list;
74a8089ea5SJohn Baldwin 	bool created;
75a8089ea5SJohn Baldwin 	int error, s;
76a8089ea5SJohn Baldwin 
77a8089ea5SJohn Baldwin 	memset(&hints, 0, sizeof(hints));
78a8089ea5SJohn Baldwin 	hints.ai_flags = AI_PASSIVE;
79a8089ea5SJohn Baldwin 	hints.ai_family = AF_UNSPEC;
80a8089ea5SJohn Baldwin 	hints.ai_protocol = IPPROTO_TCP;
81a8089ea5SJohn Baldwin 	error = getaddrinfo(NULL, port, &hints, &list);
82a8089ea5SJohn Baldwin 	if (error != 0)
83a8089ea5SJohn Baldwin 		errx(1, "%s", gai_strerror(error));
84a8089ea5SJohn Baldwin 	created = false;
85a8089ea5SJohn Baldwin 
86a8089ea5SJohn Baldwin 	for (ai = list; ai != NULL; ai = ai->ai_next) {
87a8089ea5SJohn Baldwin 		s = socket(ai->ai_family, ai->ai_socktype, ai->ai_protocol);
88a8089ea5SJohn Baldwin 		if (s == -1)
89a8089ea5SJohn Baldwin 			continue;
90a8089ea5SJohn Baldwin 
91a8089ea5SJohn Baldwin 		if (bind(s, ai->ai_addr, ai->ai_addrlen) != 0) {
92a8089ea5SJohn Baldwin 			close(s);
93a8089ea5SJohn Baldwin 			continue;
94a8089ea5SJohn Baldwin 		}
95a8089ea5SJohn Baldwin 
96a8089ea5SJohn Baldwin 		if (discovery) {
97a8089ea5SJohn Baldwin 			register_listen_socket(kqfd, s, (void *)1);
98a8089ea5SJohn Baldwin 		} else {
99a8089ea5SJohn Baldwin 			register_listen_socket(kqfd, s, (void *)2);
100a8089ea5SJohn Baldwin 			discovery_add_io_controller(s, subnqn);
101a8089ea5SJohn Baldwin 		}
102a8089ea5SJohn Baldwin 		created = true;
103a8089ea5SJohn Baldwin 	}
104a8089ea5SJohn Baldwin 
105a8089ea5SJohn Baldwin 	freeaddrinfo(list);
106a8089ea5SJohn Baldwin 	if (!created)
107a8089ea5SJohn Baldwin 		err(1, "Failed to create any listen sockets");
108a8089ea5SJohn Baldwin }
109a8089ea5SJohn Baldwin 
110a8089ea5SJohn Baldwin static void
111a8089ea5SJohn Baldwin handle_connections(int kqfd)
112a8089ea5SJohn Baldwin {
113a8089ea5SJohn Baldwin 	struct kevent ev;
114a8089ea5SJohn Baldwin 	int s;
115a8089ea5SJohn Baldwin 
116a8089ea5SJohn Baldwin 	signal(SIGHUP, handle_sig);
117a8089ea5SJohn Baldwin 	signal(SIGINT, handle_sig);
118a8089ea5SJohn Baldwin 	signal(SIGQUIT, handle_sig);
119a8089ea5SJohn Baldwin 	signal(SIGTERM, handle_sig);
120a8089ea5SJohn Baldwin 
121a8089ea5SJohn Baldwin 	while (!quit) {
122a8089ea5SJohn Baldwin 		if (kevent(kqfd, NULL, 0, &ev, 1, NULL) == -1) {
123a8089ea5SJohn Baldwin 			if (errno == EINTR)
124a8089ea5SJohn Baldwin 				continue;
125a8089ea5SJohn Baldwin 			err(1, "kevent");
126a8089ea5SJohn Baldwin 		}
127a8089ea5SJohn Baldwin 
128a8089ea5SJohn Baldwin 		assert(ev.filter == EVFILT_READ);
129a8089ea5SJohn Baldwin 
130a8089ea5SJohn Baldwin 		s = accept(ev.ident, NULL, NULL);
131a8089ea5SJohn Baldwin 		if (s == -1) {
132a8089ea5SJohn Baldwin 			warn("accept");
133a8089ea5SJohn Baldwin 			continue;
134a8089ea5SJohn Baldwin 		}
135a8089ea5SJohn Baldwin 
136a8089ea5SJohn Baldwin 		switch ((uintptr_t)ev.udata) {
137a8089ea5SJohn Baldwin 		case 1:
138a8089ea5SJohn Baldwin 			handle_discovery_socket(s);
139a8089ea5SJohn Baldwin 			break;
140a8089ea5SJohn Baldwin 		case 2:
141a8089ea5SJohn Baldwin 			handle_io_socket(s);
142a8089ea5SJohn Baldwin 			break;
143a8089ea5SJohn Baldwin 		default:
144a8089ea5SJohn Baldwin 			__builtin_unreachable();
145a8089ea5SJohn Baldwin 		}
146a8089ea5SJohn Baldwin 	}
147a8089ea5SJohn Baldwin }
148a8089ea5SJohn Baldwin 
149a8089ea5SJohn Baldwin int
150a8089ea5SJohn Baldwin main(int ac, char **av)
151a8089ea5SJohn Baldwin {
152a8089ea5SJohn Baldwin 	struct pidfh *pfh;
153a8089ea5SJohn Baldwin 	const char *dport, *ioport, *transport;
154a8089ea5SJohn Baldwin 	pid_t pid;
155*399362baSJohn Baldwin 	uint64_t value;
156a8089ea5SJohn Baldwin 	int ch, error, kqfd;
157a8089ea5SJohn Baldwin 	bool daemonize;
158a8089ea5SJohn Baldwin 	static char nqn[NVMF_NQN_MAX_LEN];
159a8089ea5SJohn Baldwin 
160a8089ea5SJohn Baldwin 	/* 7.4.9.3 Default port for discovery */
161a8089ea5SJohn Baldwin 	dport = "8009";
162a8089ea5SJohn Baldwin 
163a8089ea5SJohn Baldwin 	pfh = NULL;
164a8089ea5SJohn Baldwin 	daemonize = true;
165a8089ea5SJohn Baldwin 	ioport = "0";
166a8089ea5SJohn Baldwin 	subnqn = NULL;
167a8089ea5SJohn Baldwin 	transport = "tcp";
168*399362baSJohn Baldwin 	while ((ch = getopt(ac, av, "dFgGH:Kn:P:p:t:")) != -1) {
169a8089ea5SJohn Baldwin 		switch (ch) {
170a8089ea5SJohn Baldwin 		case 'd':
171a8089ea5SJohn Baldwin 			daemonize = false;
172a8089ea5SJohn Baldwin 			break;
173a8089ea5SJohn Baldwin 		case 'F':
174a8089ea5SJohn Baldwin 			flow_control_disable = true;
175a8089ea5SJohn Baldwin 			break;
176a8089ea5SJohn Baldwin 		case 'G':
177a8089ea5SJohn Baldwin 			data_digests = true;
178a8089ea5SJohn Baldwin 			break;
179a8089ea5SJohn Baldwin 		case 'g':
180a8089ea5SJohn Baldwin 			header_digests = true;
181a8089ea5SJohn Baldwin 			break;
182*399362baSJohn Baldwin 		case 'H':
183*399362baSJohn Baldwin 			if (expand_number(optarg, &value) != 0)
184*399362baSJohn Baldwin 				errx(1, "Invalid MAXH2CDATA value %s", optarg);
185*399362baSJohn Baldwin 			if (value < 4096 || value > UINT32_MAX ||
186*399362baSJohn Baldwin 			    value % 4 != 0)
187*399362baSJohn Baldwin 				errx(1, "Invalid MAXH2CDATA value %s", optarg);
188*399362baSJohn Baldwin 			maxh2cdata = value;
189*399362baSJohn Baldwin 			break;
190a8089ea5SJohn Baldwin 		case 'K':
191a8089ea5SJohn Baldwin 			kernel_io = true;
192a8089ea5SJohn Baldwin 			break;
193a8089ea5SJohn Baldwin 		case 'n':
194a8089ea5SJohn Baldwin 			subnqn = optarg;
195a8089ea5SJohn Baldwin 			break;
196a8089ea5SJohn Baldwin 		case 'P':
197a8089ea5SJohn Baldwin 			dport = optarg;
198a8089ea5SJohn Baldwin 			break;
199a8089ea5SJohn Baldwin 		case 'p':
200a8089ea5SJohn Baldwin 			ioport = optarg;
201a8089ea5SJohn Baldwin 			break;
202a8089ea5SJohn Baldwin 		case 't':
203a8089ea5SJohn Baldwin 			transport = optarg;
204a8089ea5SJohn Baldwin 			break;
205a8089ea5SJohn Baldwin 		default:
206a8089ea5SJohn Baldwin 			usage();
207a8089ea5SJohn Baldwin 		}
208a8089ea5SJohn Baldwin 	}
209a8089ea5SJohn Baldwin 
210a8089ea5SJohn Baldwin 	av += optind;
211a8089ea5SJohn Baldwin 	ac -= optind;
212a8089ea5SJohn Baldwin 
213a8089ea5SJohn Baldwin 	if (kernel_io) {
214a8089ea5SJohn Baldwin 		if (ac > 0)
215a8089ea5SJohn Baldwin 			usage();
216a8089ea5SJohn Baldwin 		if (modfind("nvmft") == -1 && kldload("nvmft") == -1)
217a8089ea5SJohn Baldwin 			warn("couldn't load nvmft");
218a8089ea5SJohn Baldwin 	} else {
219a8089ea5SJohn Baldwin 		if (ac < 1)
220a8089ea5SJohn Baldwin 			usage();
221a8089ea5SJohn Baldwin 	}
222a8089ea5SJohn Baldwin 
223a8089ea5SJohn Baldwin 	if (strcasecmp(transport, "tcp") == 0) {
224a8089ea5SJohn Baldwin 	} else
225a8089ea5SJohn Baldwin 		errx(1, "Invalid transport %s", transport);
226a8089ea5SJohn Baldwin 
227a8089ea5SJohn Baldwin 	if (subnqn == NULL) {
228a8089ea5SJohn Baldwin 		error = nvmf_nqn_from_hostuuid(nqn);
229a8089ea5SJohn Baldwin 		if (error != 0)
230a8089ea5SJohn Baldwin 			errc(1, error, "Failed to generate NQN");
231a8089ea5SJohn Baldwin 		subnqn = nqn;
232a8089ea5SJohn Baldwin 	}
233a8089ea5SJohn Baldwin 
234a8089ea5SJohn Baldwin 	if (!kernel_io)
235a8089ea5SJohn Baldwin 		register_devices(ac, av);
236a8089ea5SJohn Baldwin 
237a8089ea5SJohn Baldwin 	init_discovery();
238a8089ea5SJohn Baldwin 	init_io(subnqn);
239a8089ea5SJohn Baldwin 
240a8089ea5SJohn Baldwin 	if (daemonize) {
241a8089ea5SJohn Baldwin 		pfh = pidfile_open(NULL, 0600, &pid);
242a8089ea5SJohn Baldwin 		if (pfh == NULL) {
243a8089ea5SJohn Baldwin 			if (errno == EEXIST)
244a8089ea5SJohn Baldwin 				errx(1, "Daemon already running, pid: %jd",
245a8089ea5SJohn Baldwin 				    (intmax_t)pid);
246a8089ea5SJohn Baldwin 			warn("Cannot open or create pidfile");
247a8089ea5SJohn Baldwin 		}
248a8089ea5SJohn Baldwin 
249a8089ea5SJohn Baldwin 		if (daemon(0, 0) != 0) {
250a8089ea5SJohn Baldwin 			pidfile_remove(pfh);
251a8089ea5SJohn Baldwin 			err(1, "Failed to fork into the background");
252a8089ea5SJohn Baldwin 		}
253a8089ea5SJohn Baldwin 
254a8089ea5SJohn Baldwin 		pidfile_write(pfh);
255a8089ea5SJohn Baldwin 	}
256a8089ea5SJohn Baldwin 
257a8089ea5SJohn Baldwin 	kqfd = kqueue();
258a8089ea5SJohn Baldwin 	if (kqfd == -1) {
259a8089ea5SJohn Baldwin 		pidfile_remove(pfh);
260a8089ea5SJohn Baldwin 		err(1, "kqueue");
261a8089ea5SJohn Baldwin 	}
262a8089ea5SJohn Baldwin 
263a8089ea5SJohn Baldwin 	create_passive_sockets(kqfd, dport, true);
264a8089ea5SJohn Baldwin 	create_passive_sockets(kqfd, ioport, false);
265a8089ea5SJohn Baldwin 
266a8089ea5SJohn Baldwin 	handle_connections(kqfd);
267a8089ea5SJohn Baldwin 	shutdown_io();
268a8089ea5SJohn Baldwin 	if (pfh != NULL)
269a8089ea5SJohn Baldwin 		pidfile_remove(pfh);
270a8089ea5SJohn Baldwin 	return (0);
271a8089ea5SJohn Baldwin }
272