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