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