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