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