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