Bug Summary

File:.build-ci/../libnvme/src/nvme/fabrics.c
Warning:line 536, column 6
Potential leak of memory pointed to by 'hid'

Annotated Source Code

Press '?' to see keyboard shortcuts

clang -cc1 -cc1 -triple x86_64-redhat-linux-gnu -O3 -analyze -disable-free -clear-ast-before-backend -disable-llvm-verifier -discard-value-names -main-file-name fabrics.c -analyzer-checker=core -analyzer-checker=apiModeling -analyzer-checker=unix -analyzer-checker=deadcode -analyzer-checker=security.insecureAPI.UncheckedReturn -analyzer-checker=security.insecureAPI.getpw -analyzer-checker=security.insecureAPI.gets -analyzer-checker=security.insecureAPI.mktemp -analyzer-checker=security.insecureAPI.mkstemp -analyzer-checker=security.insecureAPI.vfork -analyzer-checker=nullability.NullPassedToNonnull -analyzer-checker=nullability.NullReturnedFromNonnull -analyzer-output plist -w -setup-static-analyzer -mrelocation-model pic -pic-level 2 -fhalf-no-semantic-interposition -mframe-pointer=none -fmath-errno -ffp-contract=on -fno-rounding-math -mconstructor-aliases -funwind-tables=2 -target-cpu x86-64 -tune-cpu generic -debugger-tuning=gdb -fdebug-compilation-dir=/__w/nvme-cli/nvme-cli/.build-ci -fcoverage-compilation-dir=/__w/nvme-cli/nvme-cli/.build-ci -resource-dir /usr/bin/../lib/clang/22 -include /__w/nvme-cli/nvme-cli/.build-ci/nvme-config.h -I libnvme/src/libnvme3.so.1.0.0.p -I libnvme/src -I ../libnvme/src -I ccan -I ../ccan -I . -I .. -D _FILE_OFFSET_BITS=64 -D _GNU_SOURCE -U NDEBUG -internal-isystem /usr/bin/../lib/clang/22/include -internal-isystem /usr/local/include -internal-isystem /usr/bin/../lib/gcc/x86_64-redhat-linux/16/../../../../x86_64-redhat-linux/include -internal-externc-isystem /include -internal-externc-isystem /usr/include -std=gnu11 -ferror-limit 19 -fvisibility=hidden -fgnuc-version=4.2.1 -fskip-odr-check-in-gmf -fcolor-diagnostics -vectorize-loops -vectorize-slp -analyzer-opt-analyze-headers -analyzer-output=html -faddrsig -fdwarf2-cfi-asm -o /__w/nvme-cli/nvme-cli/.build-ci/scan-results/2026-09-23-073103-589-1 -x c ../libnvme/src/nvme/fabrics.c
1// SPDX-License-Identifier: LGPL-2.1-or-later
2/*
3 * This file is part of libnvme.
4 * Copyright (c) 2020 Western Digital Corporation or its affiliates.
5 *
6 * Authors: Keith Busch <keith.busch@wdc.com>
7 * Chaitanya Kulkarni <chaitanya.kulkarni@wdc.com>
8 */
9
10#include <dirent.h>
11#include <errno(*__errno_location ()).h>
12#include <fcntl.h>
13#include <fnmatch.h>
14#include <ifaddrs.h>
15#include <inttypes.h>
16#include <limits.h>
17#include <stdbool.h>
18#include <stddef.h>
19#include <stdio.h>
20#include <stdlib.h>
21#include <string.h>
22#include <strings.h>
23#include <unistd.h>
24
25#include <arpa/inet.h>
26#include <linux1/if_ether.h>
27#include <net/if.h>
28#include <netpacket/packet.h>
29#include <sys/param.h>
30#include <sys/stat.h>
31#include <sys/types.h>
32
33#include <ccan/array_size/array_size.h>
34#include <ccan/endian/endian.h>
35#include <ccan/list/list.h>
36#include <ccan/str/str.h>
37
38#include <shared/array-util.h>
39#include <shared/compiler-attributes-util.h>
40#include <shared/machine-id-util.h>
41#include <shared/nqn-util.h>
42#include <shared/string-util.h>
43#include <shared/uuid-util.h>
44
45#include <libnvme.h>
46
47#include "cleanup.h"
48#include "cleanup-linux.h"
49#include "private.h"
50#include "private-fabrics.h"
51
52const char *nvmf_dev = "/dev/nvme-fabrics";
53
54static inline void free_uri(struct libnvmf_uri **uri)
55{
56 libnvmf_uri_free(*uri);
57}
58#define __cleanup_uri__attribute__((cleanup(free_uri))) __cleanup(free_uri)__attribute__((cleanup(free_uri)))
59
60static struct libnvme_ctrl_params ctrl_params_copy(
61 const struct libnvme_ctrl_params *src)
62{
63 struct libnvme_ctrl_params params = *src;
64
65 params.host_iface = NULL((void*)0);
66 return params;
67}
68
69static struct libnvme_ctrl_params ctrl_params_dup(
70 const struct libnvme_ctrl_params *src)
71{
72 struct libnvme_ctrl_params params = *src;
73
74 params.host_iface = shr_xstrdup(src->host_iface);
75 return params;
76}
77
78static inline void cleanup_ctrl_params(struct libnvme_ctrl_params *params)
79{
80 free((char *)params->host_iface);
81}
82#define __cleanup_ctrl_params__attribute__((cleanup(cleanup_ctrl_params))) __cleanup(cleanup_ctrl_params)__attribute__((cleanup(cleanup_ctrl_params)))
83
84const char *arg_str(const char * const *strings,
85 size_t array_size, size_t idx)
86{
87 if (idx < array_size && strings[idx])
88 return strings[idx];
89 return "unrecognized";
90}
91
92#define NVMF_HOSTID_SIZE37 37
93
94#define NVMF_HOSTNQN_FILE"/usr/local/etc" "/nvme/hostnqn" SYSCONFDIR"/usr/local/etc" "/nvme/hostnqn"
95#define NVMF_HOSTID_FILE"/usr/local/etc" "/nvme/hostid" SYSCONFDIR"/usr/local/etc" "/nvme/hostid"
96
97static int uuid_from_device_tree(struct libnvme_global_ctx *ctx,
98 char *system_uuid)
99{
100 __cleanup_fd__attribute__((cleanup(shr_cleanup_fd))) int f = -1;
101 ssize_t len;
102
103 f = open(libnvme_uuid_ibm_filename(ctx), O_RDONLY00);
104 if (f < 0)
105 return -ENXIO6;
106
107 memset(system_uuid, 0, NVME_UUID_LEN_STRING37);
108 len = read(f, system_uuid, NVME_UUID_LEN_STRING37 - 1);
109 if (len < 0)
110 return -ENXIO6;
111
112 return strlen(system_uuid) ? 0 : -ENXIO6;
113}
114
115/*
116 * See System Management BIOS (SMBIOS) Reference Specification
117 * https://www.dmtf.org/sites/default/files/standards/documents/DSP0134_3.2.0.pdf
118 */
119#define DMI_SYSTEM_INFORMATION1 1
120
121static bool_Bool is_dmi_uuid_valid(const char *buf, size_t len)
122{
123 int i;
124
125 /* UUID bytes are from byte 8 to 23 */
126 if (len < 24)
127 return false0;
128
129 /* Test it's a invalid UUID with all zeros */
130 for (i = 8; i < 24; i++) {
131 if (buf[i])
132 break;
133 }
134 if (i == 24)
135 return false0;
136
137 return true1;
138}
139
140static int read_file(char *filename, char *buf, size_t size)
141{
142 __cleanup_fd__attribute__((cleanup(shr_cleanup_fd))) int f = -1;
143 int len;
144
145 f = open(filename, O_RDONLY00);
146 if (f < 0)
147 return -errno(*__errno_location ());
148 len = read(f, buf, size - 1);
149 if (len < 0)
150 return -errno(*__errno_location ());
151 buf[len] = 0;
152
153 return len;
154}
155
156static int uuid_from_dmi_entries(struct libnvme_global_ctx *ctx,
157 char *system_uuid)
158{
159 __cleanup_dir__attribute__((cleanup(cleanup_dir))) DIR *d = NULL((void*)0);
160 const char *entries_dir = libnvme_dmi_entries_dir(ctx);
161 char filename[PATH_MAX4096];
162 struct dirent *de;
163 char buf[513] = {0};
164 int len, type;
165
166 system_uuid[0] = '\0';
167 d = opendir(entries_dir);
168 if (!d)
169 return -ENXIO6;
170 while ((de = readdir(d))) {
171 if (de->d_name[0] == '.')
172 continue;
173 snprintf(filename, sizeof(filename), "%s/%s/type", entries_dir,
174 de->d_name);
175 len = read_file(filename, buf, sizeof(buf));
176 if (len <= 0)
177 continue;
178 if (sscanf(buf, "%d", &type) != 1)
179 continue;
180 if (type != DMI_SYSTEM_INFORMATION1)
181 continue;
182 snprintf(filename, sizeof(filename), "%s/%s/raw", entries_dir,
183 de->d_name);
184 len = read_file(filename, buf, sizeof(buf));
185 if (len <= 0)
186 continue;
187
188 if (!is_dmi_uuid_valid(buf, len))
189 continue;
190
191 /* Sigh. https://en.wikipedia.org/wiki/Overengineering */
192 /* DMTF SMBIOS 3.0 Section 7.2.1 System UUID */
193 sprintf(system_uuid,
194 "%02x%02x%02x%02x-%02x%02x-%02x%02x-%02x%02x-"
195 "%02x%02x%02x%02x%02x%02x",
196 (uint8_t)buf[8 + 3], (uint8_t)buf[8 + 2],
197 (uint8_t)buf[8 + 1], (uint8_t)buf[8 + 0],
198 (uint8_t)buf[8 + 5], (uint8_t)buf[8 + 4],
199 (uint8_t)buf[8 + 7], (uint8_t)buf[8 + 6],
200 (uint8_t)buf[8 + 8], (uint8_t)buf[8 + 9],
201 (uint8_t)buf[8 + 10], (uint8_t)buf[8 + 11],
202 (uint8_t)buf[8 + 12], (uint8_t)buf[8 + 13],
203 (uint8_t)buf[8 + 14], (uint8_t)buf[8 + 15]);
204 break;
205 }
206 return strlen(system_uuid) ? 0 : -ENXIO6;
207}
208
209/**
210 * uuid_from_product_uuid() - Get system UUID from product_uuid
211 * @ctx: Global context, for the sysfs path.
212 * @system_uuid: Where to save the system UUID.
213 *
214 * Return: 0 on success, -ENXIO otherwise.
215 */
216static int uuid_from_product_uuid(struct libnvme_global_ctx *ctx,
217 char *system_uuid)
218{
219 __cleanup_file__attribute__((cleanup(shr_cleanup_file))) FILE *stream = NULL((void*)0);
220
221 stream = fopen(libnvme_dmi_product_uuid_filename(ctx), "re");
222 if (!stream)
223 return -ENXIO6;
224
225 system_uuid[0] = '\0';
226
227 /* The kernel is handling the byte swapping according DMTF
228 * SMBIOS 3.0 Section 7.2.1 System UUID */
229
230 /*
231 * Expect exactly:
232 * xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx
233 */
234 if (!fgets(system_uuid, NVME_UUID_LEN_STRING37, stream))
235 return -ENXIO6;
236
237 if (strlen(system_uuid) != NVME_UUID_LEN_STRING37 - 1)
238 return -ENXIO6;
239
240 if (system_uuid[8] != '-' || system_uuid[13] != '-' ||
241 system_uuid[18] != '-' || system_uuid[23] != '-')
242 return -ENXIO6;
243
244 system_uuid[NVME_UUID_LEN_STRING37 - 1] = '\0';
245
246 return 0;
247}
248
249/**
250 * uuid_from_machine_id() - Derive a system UUID from the local machine ID
251 * @ctx: Global context, for the file path.
252 * @system_uuid: Where to save the system UUID.
253 *
254 * The machine ID identifies an installation rather than the hardware, so it
255 * is less stable than DMI or the device tree. It is still deterministic, and
256 * unlike them it is readable without privileges.
257 *
258 * Return: 0 on success, negative errno otherwise.
259 */
260static int uuid_from_machine_id(struct libnvme_global_ctx *ctx,
261 char *system_uuid)
262{
263 /*
264 * Fixed application ID for nvme-cli's use of the local machine ID.
265 * Chosen once, at random, and part of the derivation: changing it
266 * changes the host identifier of every machine that has no DMI or
267 * device tree UUID.
268 */
269 static const unsigned char app_id[SHR_UUID_LEN16] = {
270 0x85, 0x3c, 0x32, 0x20, 0xca, 0x64, 0x4d, 0xad,
271 0xa0, 0x6f, 0x1d, 0xc7, 0x52, 0xe3, 0x22, 0x63
272 };
273 unsigned char uuid[NVME_UUID_LEN16];
274 int ret;
275
276 ret = shr_machine_id_app_specific(libnvme_machine_id_filename(ctx),
277 app_id, uuid);
278 if (ret)
279 return ret;
280
281 libnvme_uuid_to_string(uuid, system_uuid);
282
283 return 0;
284}
285
286/**
287 * uuid_from_dmi() - read system UUID
288 * @ctx: Global context, for the sysfs paths.
289 * @system_uuid: buffer for the UUID
290 *
291 * The system UUID can be read from two different locations:
292 *
293 * 1) /sys/class/dmi/id/product_uuid
294 * 2) /sys/firmware/dmi/entries
295 *
296 * Note that the second location is not present on Debian-based systems.
297 *
298 * Return: 0 on success, negative errno otherwise.
299 */
300static int uuid_from_dmi(struct libnvme_global_ctx *ctx, char *system_uuid)
301{
302 int ret = uuid_from_product_uuid(ctx, system_uuid);
303 if (ret != 0)
304 ret = uuid_from_dmi_entries(ctx, system_uuid);
305 return ret;
306}
307
308/*
309 * Virtual machines and some firmware report a placeholder instead of a real
310 * identifier. Every machine reporting the same placeholder would end up with
311 * the same host identifier.
312 *
313 * shr_hostid_valid() rejects the all-zeros UUID. The all-ones UUID is the
314 * other common placeholder, but it is a legitimate value to configure by
315 * hand, so it is refused here rather than in the shared validator.
316 */
317static bool_Bool hostid_source_usable(const char *system_uuid)
318{
319 return shr_hostid_valid(system_uuid) &&
320 !shr_streqcase0(system_uuid,
321 "ffffffff-ffff-ffff-ffff-ffffffffffff");
322}
323
324__shr_public__attribute__((visibility("default"))) char *libnvmf_generate_hostid(struct libnvme_global_ctx *ctx)
325{
326 /*
327 * Ordered from most to least stable. The firmware sources describe
328 * the hardware and survive a reinstall; the machine ID describes the
329 * installation and does not.
330 */
331 static const struct {
332 const char *name;
333 int (*get)(struct libnvme_global_ctx *ctx, char *system_uuid);
334 } hostid_sources[] = {
335 { "DMI", uuid_from_dmi },
336 { "device tree", uuid_from_device_tree },
337 { "machine ID", uuid_from_machine_id },
338 };
339 char uuid_str[NVME_UUID_LEN_STRING37];
340 unsigned char uuid[NVME_UUID_LEN16];
341 size_t i;
342
343 if (!ctx)
344 return NULL((void*)0);
345
346 for (i = 0; i < ARRAY_SIZE(hostid_sources)(sizeof(hostid_sources) / sizeof((hostid_sources)[0]) + 0); i++) {
347 if (hostid_sources[i].get(ctx, uuid_str))
348 continue;
349
350 if (!hostid_source_usable(uuid_str)) {
351 libnvme_msg(ctx, LIBNVME_LOG_DEBUG,__libnvme_msg(ctx, LIBNVME_LOG_DEBUG, ((void*)0), "%s reports an unusable host identifier '%s'\n"
, hostid_sources[i].name, uuid_str)
352 "%s reports an unusable host identifier '%s'\n",__libnvme_msg(ctx, LIBNVME_LOG_DEBUG, ((void*)0), "%s reports an unusable host identifier '%s'\n"
, hostid_sources[i].name, uuid_str)
353 hostid_sources[i].name, uuid_str)__libnvme_msg(ctx, LIBNVME_LOG_DEBUG, ((void*)0), "%s reports an unusable host identifier '%s'\n"
, hostid_sources[i].name, uuid_str)
;
354 continue;
355 }
356
357 libnvme_msg(ctx, LIBNVME_LOG_DEBUG,__libnvme_msg(ctx, LIBNVME_LOG_DEBUG, ((void*)0), "host identifier taken from %s\n"
, hostid_sources[i].name)
358 "host identifier taken from %s\n",__libnvme_msg(ctx, LIBNVME_LOG_DEBUG, ((void*)0), "host identifier taken from %s\n"
, hostid_sources[i].name)
359 hostid_sources[i].name)__libnvme_msg(ctx, LIBNVME_LOG_DEBUG, ((void*)0), "host identifier taken from %s\n"
, hostid_sources[i].name)
;
360
361 return strdup(uuid_str);
362 }
363
364 libnvme_msg(ctx, LIBNVME_LOG_WARN,__libnvme_msg(ctx, LIBNVME_LOG_WARN, ((void*)0), "no stable host identifier available, using a random one\n"
)
365 "no stable host identifier available, using a random one\n")__libnvme_msg(ctx, LIBNVME_LOG_WARN, ((void*)0), "no stable host identifier available, using a random one\n"
)
;
366
367 if (libnvme_random_uuid(uuid) < 0)
368 memset(uuid, 0, NVME_UUID_LEN16);
369 libnvme_uuid_to_string(uuid, uuid_str);
370
371 return strdup(uuid_str);
372}
373
374__shr_public__attribute__((visibility("default"))) char *libnvmf_generate_hostnqn_from_hostid(
375 struct libnvme_global_ctx *ctx, char *hostid)
376{
377 char *hid = NULL((void*)0);
378 char *hostnqn;
379 int ret;
380
381 if (!ctx)
382 return NULL((void*)0);
383
384 if (!hostid) {
385 hostid = hid = libnvmf_generate_hostid(ctx);
386 if (!hostid)
387 return NULL((void*)0);
388 }
389
390 ret = asprintf(&hostnqn, "nqn.2014-08.org.nvmexpress:uuid:%s", hostid);
391 free(hid);
392
393 return (ret < 0) ? NULL((void*)0) : hostnqn;
394}
395
396__shr_public__attribute__((visibility("default"))) char *libnvmf_generate_hostnqn(struct libnvme_global_ctx *ctx)
397{
398 return libnvmf_generate_hostnqn_from_hostid(ctx, NULL((void*)0));
399}
400
401static char *nvmf_read_file(const char *f, int len)
402{
403 char buf[len];
404 __cleanup_fd__attribute__((cleanup(shr_cleanup_fd))) int fd = -1;
405 int ret;
406
407 fd = open(f, O_RDONLY00);
408 if (fd < 0)
409 return NULL((void*)0);
410
411 memset(buf, 0, len);
412 ret = read(fd, buf, len - 1);
413
414 if (ret < 0 || !strlen(buf))
415 return NULL((void*)0);
416 return strndup(buf, strcspn(buf, "\n"));
417}
418
419__shr_public__attribute__((visibility("default"))) char *libnvmf_read_hostnqn(struct libnvme_global_ctx *ctx)
420{
421 char *val;
422
423 if (!ctx)
424 return NULL((void*)0);
425
426 if (shr_nqn_valid(ctx->hostnqn))
427 return strdup(ctx->hostnqn);
428
429 val = nvmf_read_file(NVMF_HOSTNQN_FILE"/usr/local/etc" "/nvme/hostnqn", NVMF_NQN_SIZE223);
430 if (shr_nqn_valid(val))
431 return val;
432
433 if (val) {
434 libnvme_msg(ctx, LIBNVME_LOG_ERR,__libnvme_msg(ctx, LIBNVME_LOG_ERR, ((void*)0), "%s does not contain a valid host NQN\n"
, "/usr/local/etc" "/nvme/hostnqn")
435 "%s does not contain a valid host NQN\n",__libnvme_msg(ctx, LIBNVME_LOG_ERR, ((void*)0), "%s does not contain a valid host NQN\n"
, "/usr/local/etc" "/nvme/hostnqn")
436 NVMF_HOSTNQN_FILE)__libnvme_msg(ctx, LIBNVME_LOG_ERR, ((void*)0), "%s does not contain a valid host NQN\n"
, "/usr/local/etc" "/nvme/hostnqn")
;
437 free(val);
438 }
439
440 return NULL((void*)0);
441}
442
443__shr_public__attribute__((visibility("default"))) char *libnvmf_read_hostid(struct libnvme_global_ctx *ctx)
444{
445 char *val;
446
447 if (!ctx
10.1
'ctx' is non-null
)
11
Taking false branch
448 return NULL((void*)0);
449
450 if (shr_hostid_valid(ctx->hostid))
12
Assuming the condition is true
13
Taking true branch
451 return strdup(ctx->hostid);
14
Memory is allocated
452
453 val = nvmf_read_file(NVMF_HOSTID_FILE"/usr/local/etc" "/nvme/hostid", NVMF_HOSTID_SIZE37);
454 if (shr_hostid_valid(val))
455 return val;
456
457 if (val) {
458 libnvme_msg(ctx, LIBNVME_LOG_ERR,__libnvme_msg(ctx, LIBNVME_LOG_ERR, ((void*)0), "%s does not contain a valid host identifier\n"
, "/usr/local/etc" "/nvme/hostid")
459 "%s does not contain a valid host identifier\n",__libnvme_msg(ctx, LIBNVME_LOG_ERR, ((void*)0), "%s does not contain a valid host identifier\n"
, "/usr/local/etc" "/nvme/hostid")
460 NVMF_HOSTID_FILE)__libnvme_msg(ctx, LIBNVME_LOG_ERR, ((void*)0), "%s does not contain a valid host identifier\n"
, "/usr/local/etc" "/nvme/hostid")
;
461 free(val);
462 }
463
464 return NULL((void*)0);
465}
466
467__shr_public__attribute__((visibility("default"))) int libnvmf_host_get_ids(struct libnvme_global_ctx *ctx,
468 const char *hostnqn_arg, const char *hostid_arg,
469 char **hostnqn, char **hostid)
470{
471 __cleanup_free__attribute__((cleanup(shr_freep))) char *nqn = NULL((void*)0);
472 __cleanup_free__attribute__((cleanup(shr_freep))) char *hid = NULL((void*)0);
473 __cleanup_free__attribute__((cleanup(shr_freep))) char *hnqn = NULL((void*)0);
474 struct libnvme_host *h;
475
476 if (!ctx)
1
Assuming 'ctx' is non-null
2
Taking false branch
477 return -EINVAL22;
478
479 /* command line argumments */
480 if (hostid_arg)
3
Assuming 'hostid_arg' is null
4
Taking false branch
481 hid = strdup(hostid_arg);
482 if (hostnqn_arg)
5
Assuming 'hostnqn_arg' is non-null
6
Taking true branch
483 hnqn = strdup(hostnqn_arg);
484
485 /* first host already resolved in ctx->hosts, if any */
486 h = libnvme_first_host(ctx);
487 if (h) {
7
Assuming 'h' is null
8
Taking false branch
488 if (!hid)
489 hid = shr_xstrdup(libnvme_host_get_hostid(h));
490 if (!hnqn)
491 hnqn = shr_xstrdup(libnvme_host_get_hostnqn(h));
492 }
493
494 /* /etc/nvme/hostid and/or /etc/nvme/hostnqn */
495 if (!hid
8.1
'hid' is null
)
9
Taking true branch
496 hid = libnvmf_read_hostid(ctx);
10
Calling 'libnvmf_read_hostid'
15
Returned allocated memory
497 if (!hnqn)
16
Assuming 'hnqn' is non-null
498 hnqn = libnvmf_read_hostnqn(ctx);
499
500 /* incomplete configuration, thus derive hostid from hostnqn */
501 if (!hid && hnqn)
17
Assuming 'hid' is non-null
502 hid = libnvme_hostid_from_hostnqn(hnqn);
503
504 /*
505 * fallback to use either DMI information or device-tree. If all
506 * fails generate one
507 */
508 if (!hid
17.1
'hid' is non-null
) {
18
Taking false branch
509 hid = libnvmf_generate_hostid(ctx);
510 if (!hid)
511 return -ENOMEM12;
512
513 libnvme_msg(ctx, LIBNVME_LOG_DEBUG,__libnvme_msg(ctx, LIBNVME_LOG_DEBUG, ((void*)0), "warning: using auto generated hostid and hostnqn\n"
)
514 "warning: using auto generated hostid and hostnqn\n")__libnvme_msg(ctx, LIBNVME_LOG_DEBUG, ((void*)0), "warning: using auto generated hostid and hostnqn\n"
)
;
515 }
516
517 /* incomplete configuration, thus derive hostnqn from hostid */
518 if (!hnqn
18.1
'hnqn' is non-null
) {
19
Taking false branch
519 hnqn = libnvmf_generate_hostnqn_from_hostid(ctx, hid);
520 if (!hnqn)
521 return -ENOMEM12;
522 }
523
524 /* sanity checks */
525 nqn = libnvme_hostid_from_hostnqn(hnqn);
526 if (nqn && strcmp(nqn, hid)) {
20
Assuming 'nqn' is null
527 libnvme_msg(ctx, LIBNVME_LOG_DEBUG,__libnvme_msg(ctx, LIBNVME_LOG_DEBUG, ((void*)0), "warning: use hostid '%s' which does not match uuid in hostnqn '%s'\n"
, hid, hnqn)
528 "warning: use hostid '%s' which does not match uuid in hostnqn '%s'\n",__libnvme_msg(ctx, LIBNVME_LOG_DEBUG, ((void*)0), "warning: use hostid '%s' which does not match uuid in hostnqn '%s'\n"
, hid, hnqn)
529 hid, hnqn)__libnvme_msg(ctx, LIBNVME_LOG_DEBUG, ((void*)0), "warning: use hostid '%s' which does not match uuid in hostnqn '%s'\n"
, hid, hnqn)
;
530 }
531
532 if (hostid) {
21
Assuming 'hostid' is null
22
Taking false branch
533 *hostid = hid;
534 hid = NULL((void*)0);
535 }
536 if (hostnqn) {
23
Potential leak of memory pointed to by 'hid'
537 *hostnqn = hnqn;
538 hnqn = NULL((void*)0);
539 }
540
541 return 0;
542}
543
544int _libnvmf_persistent_from_str(const char *str, enum libnvmf_persistent *val)
545{
546 if (!str || !val)
547 return -EINVAL22;
548
549 if (!strcasecmp(str, "no"))
550 *val = LIBNVMF_PERSISTENT_NO;
551 else if (!strcasecmp(str, "auto"))
552 *val = LIBNVMF_PERSISTENT_AUTO;
553 else if (!strcasecmp(str, "force"))
554 *val = LIBNVMF_PERSISTENT_FORCE;
555 else
556 return -EINVAL22;
557
558 return 0;
559}
560
561/**
562 * libnvmf_context_set_persistent() - Set the discovery controller
563 * persistence mode.
564 * @fctx: The &struct libnvmf_context instance to update.
565 * @persistent: One of "no", "auto", "force" (case-insensitive).
566 *
567 * Return: 0 on success, -EINVAL if @persistent matches none of the
568 * accepted values.
569 */
570__shr_public__attribute__((visibility("default"))) int libnvmf_context_set_persistent(struct libnvmf_context *fctx,
571 const char *persistent)
572{
573 if (!fctx)
574 return -EINVAL22;
575
576 return _libnvmf_persistent_from_str(persistent, &fctx->persistent);
577}
578
579/**
580 * libnvmf_context_get_persistent() - Get the discovery controller
581 * persistence mode.
582 * @fctx: The &struct libnvmf_context instance to query.
583 *
584 * Return: "no", "auto", or "force" if explicitly configured; NULL if not
585 * (behaves the same as "no" when applied to a live connection, or if
586 * @fctx is NULL).
587 */
588__shr_public__attribute__((visibility("default"))) const char *libnvmf_context_get_persistent(
589 const struct libnvmf_context *fctx)
590{
591 if (!fctx)
592 return NULL((void*)0);
593
594 switch (fctx->persistent) {
595 case LIBNVMF_PERSISTENT_NO:
596 return "no";
597 case LIBNVMF_PERSISTENT_AUTO:
598 return "auto";
599 case LIBNVMF_PERSISTENT_FORCE:
600 return "force";
601 case LIBNVMF_PERSISTENT_UNSET:
602 default:
603 return NULL((void*)0);
604 }
605}
606
607const char * const trtypes[] = {
608 [NVMF_TRTYPE_RDMA] = "rdma",
609 [NVMF_TRTYPE_FC] = "fc",
610 [NVMF_TRTYPE_TCP] = "tcp",
611 [NVMF_TRTYPE_LOOP] = "loop",
612};
613
614__shr_public__attribute__((visibility("default"))) const char *libnvmf_trtype_str(__u8 trtype)
615{
616 return arg_str(trtypes, ARRAY_SIZE(trtypes)(sizeof(trtypes) / sizeof((trtypes)[0]) + 0), trtype);
617}
618
619static const char * const adrfams[] = {
620 [NVMF_ADDR_FAMILY_PCI] = "pci",
621 [NVMF_ADDR_FAMILY_IP4] = "ipv4",
622 [NVMF_ADDR_FAMILY_IP6] = "ipv6",
623 [NVMF_ADDR_FAMILY_IB] = "infiniband",
624 [NVMF_ADDR_FAMILY_FC] = "fibre-channel",
625};
626
627__shr_public__attribute__((visibility("default"))) const char *libnvmf_adrfam_str(__u8 adrfam)
628{
629 return arg_str(adrfams, ARRAY_SIZE(adrfams)(sizeof(adrfams) / sizeof((adrfams)[0]) + 0), adrfam);
630}
631
632static const char * const subtypes[] = {
633 [NVME_NQN_DISC] = "discovery subsystem referral",
634 [NVME_NQN_NVME] = "nvme subsystem",
635 [NVME_NQN_CURR] = "current discovery subsystem",
636};
637
638__shr_public__attribute__((visibility("default"))) const char *libnvmf_subtype_str(__u8 subtype)
639{
640 return arg_str(subtypes, ARRAY_SIZE(subtypes)(sizeof(subtypes) / sizeof((subtypes)[0]) + 0), subtype);
641}
642
643static const char * const treqs[] = {
644 [NVMF_TREQ_NOT_SPECIFIED] = "not specified",
645 [NVMF_TREQ_REQUIRED] = "required",
646 [NVMF_TREQ_NOT_REQUIRED] = "not required",
647 [NVMF_TREQ_NOT_SPECIFIED |
648 NVMF_TREQ_DISABLE_SQFLOW] = "not specified, "
649 "sq flow control disable supported",
650 [NVMF_TREQ_REQUIRED |
651 NVMF_TREQ_DISABLE_SQFLOW] = "required, "
652 "sq flow control disable supported",
653 [NVMF_TREQ_NOT_REQUIRED |
654 NVMF_TREQ_DISABLE_SQFLOW] = "not required, "
655 "sq flow control disable supported",
656};
657
658__shr_public__attribute__((visibility("default"))) const char *libnvmf_treq_str(__u8 treq)
659{
660 return arg_str(treqs, ARRAY_SIZE(treqs)(sizeof(treqs) / sizeof((treqs)[0]) + 0), treq);
661}
662
663static const char * const eflags_strings[] = {
664 [NVMF_DISC_EFLAGS_NONE] = "none",
665 [NVMF_DISC_EFLAGS_EPCSD] = "explicit discovery connections",
666 [NVMF_DISC_EFLAGS_DUPRETINFO] = "duplicate discovery information",
667 [NVMF_DISC_EFLAGS_EPCSD |
668 NVMF_DISC_EFLAGS_DUPRETINFO] = "explicit discovery connections, "
669 "duplicate discovery information",
670 [NVMF_DISC_EFLAGS_NCC] = "no cdc connectivity",
671 [NVMF_DISC_EFLAGS_EPCSD |
672 NVMF_DISC_EFLAGS_NCC] = "explicit discovery connections, "
673 "no cdc connectivity",
674 [NVMF_DISC_EFLAGS_DUPRETINFO |
675 NVMF_DISC_EFLAGS_NCC] = "duplicate discovery information, "
676 "no cdc connectivity",
677 [NVMF_DISC_EFLAGS_EPCSD |
678 NVMF_DISC_EFLAGS_DUPRETINFO |
679 NVMF_DISC_EFLAGS_NCC] = "explicit discovery connections, "
680 "duplicate discovery information, "
681 "no cdc connectivity",
682};
683
684__shr_public__attribute__((visibility("default"))) const char *libnvmf_eflags_str(__u16 eflags)
685{
686 return arg_str(eflags_strings, ARRAY_SIZE(eflags_strings)(sizeof(eflags_strings) / sizeof((eflags_strings)[0]) + 0), eflags);
687}
688
689static const char * const sectypes[] = {
690 [NVMF_TCP_SECTYPE_NONE] = "none",
691 [NVMF_TCP_SECTYPE_TLS] = "tls",
692 [NVMF_TCP_SECTYPE_TLS13] = "tls13",
693};
694
695__shr_public__attribute__((visibility("default"))) const char *libnvmf_sectype_str(__u8 sectype)
696{
697 return arg_str(sectypes, ARRAY_SIZE(sectypes)(sizeof(sectypes) / sizeof((sectypes)[0]) + 0), sectype);
698}
699
700static const char * const prtypes[] = {
701 [NVMF_RDMA_PRTYPE_NOT_SPECIFIED] = "not specified",
702 [NVMF_RDMA_PRTYPE_IB] = "infiniband",
703 [NVMF_RDMA_PRTYPE_ROCE] = "roce",
704 [NVMF_RDMA_PRTYPE_ROCEV2] = "roce-v2",
705 [NVMF_RDMA_PRTYPE_IWARP] = "iwarp",
706};
707
708__shr_public__attribute__((visibility("default"))) const char *libnvmf_prtype_str(__u8 prtype)
709{
710 return arg_str(prtypes, ARRAY_SIZE(prtypes)(sizeof(prtypes) / sizeof((prtypes)[0]) + 0), prtype);
711}
712
713static const char * const qptypes[] = {
714 [NVMF_RDMA_QPTYPE_CONNECTED] = "connected",
715 [NVMF_RDMA_QPTYPE_DATAGRAM] = "datagram",
716};
717
718__shr_public__attribute__((visibility("default"))) const char *libnvmf_qptype_str(__u8 qptype)
719{
720 return arg_str(qptypes, ARRAY_SIZE(qptypes)(sizeof(qptypes) / sizeof((qptypes)[0]) + 0), qptype);
721}
722
723static const char * const cms[] = {
724 [NVMF_RDMA_CMS_RDMA_CM] = "rdma-cm",
725};
726
727__shr_public__attribute__((visibility("default"))) const char *libnvmf_cms_str(__u8 cm)
728{
729 return arg_str(cms, ARRAY_SIZE(cms)(sizeof(cms) / sizeof((cms)[0]) + 0), cm);
730}
731
732void libnvmf_default_config(struct libnvme_fabrics_config *cfg)
733{
734 cfg->tos = -1;
735 cfg->ctrl_loss_tmo = NVMF_DEF_CTRL_LOSS_TMO600;
736}
737
738__shr_public__attribute__((visibility("default"))) int libnvmf_context_create(struct libnvme_global_ctx *ctx,
739 bool_Bool (*decide_retry)(struct libnvmf_context *fctx, int err,
740 void *user_data),
741 void (*connected)(struct libnvmf_context *fctx,
742 struct libnvme_ctrl *c, void *user_data),
743 void (*already_connected)(struct libnvmf_context *fctx,
744 struct libnvme_host *host, const char *subsysnqn,
745 const char *transport, const char *traddr,
746 const char *trsvcid, void *user_data),
747 void *user_data, struct libnvmf_context **fctxp)
748{
749 struct libnvmf_context *fctx;
750
751 fctx = calloc(1, sizeof(*fctx));
752 if (!fctx)
753 return -ENOMEM12;
754
755 fctx->ctx = ctx;
756
757 libnvmf_default_config(&fctx->ctrl_params.cfg);
758
759 fctx->hooks.decide_retry = decide_retry;
760 fctx->hooks.connected = connected;
761 fctx->hooks.already_connected = already_connected;
762
763 fctx->hooks.user_data = user_data;
764
765 *fctxp = fctx;
766 return 0;
767}
768
769__shr_public__attribute__((visibility("default"))) void libnvmf_context_free(struct libnvmf_context *fctx)
770{
771 if (!fctx)
772 return;
773
774 free(fctx->nbft_path);
775 free(fctx->hostnqn);
776 free(fctx->hostid);
777 free(fctx->tls_key);
778 free(fctx);
779}
780
781__shr_public__attribute__((visibility("default"))) int libnvmf_context_set_discovery_hooks(
782 struct libnvmf_context *fctx,
783 void (*discovery_log)(struct libnvmf_context *fctx,
784 const struct nvmf_discovery_log *log,
785 uint64_t numrec, void *user_data))
786{
787 fctx->hooks.discovery_log = discovery_log;
788
789 return 0;
790}
791
792
793
794__shr_public__attribute__((visibility("default"))) int libnvmf_context_set_connection(
795 struct libnvmf_context *fctx, const char *subsysnqn,
796 const char *transport, const char *traddr, const char *trsvcid,
797 const char *host_traddr, const char *host_iface)
798{
799 fctx->ctrl_params.subsysnqn = subsysnqn;
800 fctx->ctrl_params.transport = transport;
801 fctx->ctrl_params.traddr = traddr;
802 fctx->ctrl_params.trsvcid = trsvcid;
803 fctx->ctrl_params.host_traddr = host_traddr;
804 fctx->ctrl_params.host_iface = host_iface;
805
806 return 0;
807}
808
809static const char *hostid_from_hostnqn(const char *hostnqn)
810{
811 const char *match;
812
813 if (!hostnqn)
814 return NULL((void*)0);
815
816 match = strstr(hostnqn, "uuid:")_Generic (0 ? (hostnqn) : (void *) 1, const void *: (const char
*) (strstr (hostnqn, "uuid:")), default: strstr (hostnqn, "uuid:"
))
;
817 if (!match)
818 return NULL((void*)0);
819
820 return match + strlen("uuid:");
821}
822
823__shr_public__attribute__((visibility("default"))) int libnvmf_context_set_hostnqn(struct libnvmf_context *fctx,
824 const char *hostnqn, const char *hostid)
825{
826 char *hnqn;
827 char *hid;
828
829 if (!hostnqn)
830 return -EINVAL22;
831
832 hnqn = strdup(hostnqn);
833 if (!hnqn)
834 return -ENOMEM12;
835
836 if (!hostid)
837 hostid = hostid_from_hostnqn(hostnqn);
838
839 if (!hostid) {
840 free(hnqn);
841 return -EINVAL22;
842 }
843
844 hid = strdup(hostid);
845 if (!hid) {
846 free(hnqn);
847 return -ENOMEM12;
848 }
849
850 free(fctx->hostnqn);
851 free(fctx->hostid);
852 fctx->hostnqn = hnqn;
853 fctx->hostid = hid;
854
855 return 0;
856}
857
858__shr_public__attribute__((visibility("default"))) int libnvmf_context_set_connection_from_tid(
859 struct libnvmf_context *fctx, const struct libnvmf_tid *tid)
860{
861 if (!fctx || !tid)
862 return -EINVAL22;
863
864 fctx->ctrl_params.subsysnqn = tid->subsysnqn;
865 fctx->ctrl_params.transport = tid->transport;
866 fctx->ctrl_params.traddr = tid->traddr;
867 fctx->ctrl_params.trsvcid = tid->trsvcid;
868 fctx->ctrl_params.host_traddr = tid->host_traddr;
869 fctx->ctrl_params.host_iface = tid->host_iface;
870
871 return libnvmf_context_set_hostnqn(fctx, tid->hostnqn, tid->hostid);
872}
873
874__shr_public__attribute__((visibility("default"))) int libnvmf_context_set_crypto(struct libnvmf_context *fctx,
875 const char *hostkey, const char *ctrlkey,
876 const char *keyring, const char *tls_key,
877 const char *tls_key_identity)
878{
879 int err;
880
881 fctx->hostkey = hostkey;
882 fctx->ctrlkey = ctrlkey;
883 fctx->keyring = keyring;
884 fctx->tls_key_identity = tls_key_identity;
885
886 if (!tls_key)
887 return 0;
888
889 if (!strncmp(tls_key, "pin:", 4)) {
890 __cleanup_free__attribute__((cleanup(shr_freep))) unsigned char *raw_secret = NULL((void*)0);
891 __cleanup_free__attribute__((cleanup(shr_freep))) char *encoded_key = NULL((void*)0);
892 int key_len = 32;
893
894 err = libnvmf_create_raw_secret(fctx->ctx, tls_key,
895 key_len, &raw_secret);
896 if (err)
897 return err;
898
899 err = libnvmf_export_tls_key(fctx->ctx, raw_secret,
900 key_len, &encoded_key);
901 if (err)
902 return err;
903
904 fctx->tls_key = encoded_key;
905 encoded_key = NULL((void*)0);
906 return 0;
907 }
908
909 free(fctx->tls_key);
910 fctx->tls_key = strdup(tls_key);
911 return 0;
912}
913
914__shr_public__attribute__((visibility("default"))) int libnvmf_context_set_device(
915 struct libnvmf_context *fctx, const char *device)
916{
917 fctx->device = device;
918
919 return 0;
920}
921
922__shr_public__attribute__((visibility("default"))) int libnvmf_context_set_devid_file(
923 struct libnvmf_context *fctx, const char *devid_file)
924{
925 fctx->devid_file = devid_file;
926
927 return 0;
928}
929
930/*
931 * O_NOFOLLOW guards the one hazard the caller can't see: a symlink at the
932 * final path component. O_TRUNC is deliberately absent -- a name recorded
933 * by an earlier successful connect must survive a failed one.
934 *
935 * Return: an open fd, or -errno.
936 */
937static int open_devid_file(struct libnvmf_context *fctx)
938{
939 int fd;
940
941 fd = open(fctx->devid_file,
942 O_WRONLY01 | O_CREAT0100 | O_CLOEXEC02000000 | O_NOFOLLOW0400000, 0644);
943 if (fd < 0) {
944 libnvme_msg(fctx->ctx, LIBNVME_LOG_ERR,__libnvme_msg(fctx->ctx, LIBNVME_LOG_ERR, ((void*)0), "failed to open devid-file %s: %s\n"
, fctx->devid_file, libnvme_strerror((*__errno_location ()
)))
945 "failed to open devid-file %s: %s\n",__libnvme_msg(fctx->ctx, LIBNVME_LOG_ERR, ((void*)0), "failed to open devid-file %s: %s\n"
, fctx->devid_file, libnvme_strerror((*__errno_location ()
)))
946 fctx->devid_file, libnvme_strerror(errno))__libnvme_msg(fctx->ctx, LIBNVME_LOG_ERR, ((void*)0), "failed to open devid-file %s: %s\n"
, fctx->devid_file, libnvme_strerror((*__errno_location ()
)))
;
947 return -errno(*__errno_location ());
948 }
949
950 return fd;
951}
952
953static void write_devid_file(struct libnvmf_context *fctx, int fd,
954 struct libnvme_ctrl *c)
955{
956 if (fd < 0 || !c)
957 return;
958
959 if (ftruncate(fd, 0) < 0 ||
960 dprintf(fd, "%s\n", libnvme_ctrl_get_name(c)) < 0)
961 libnvme_msg(fctx->ctx, LIBNVME_LOG_WARN,__libnvme_msg(fctx->ctx, LIBNVME_LOG_WARN, ((void*)0), "failed to write devid-file %s: %s\n"
, fctx->devid_file, libnvme_strerror((*__errno_location ()
)))
962 "failed to write devid-file %s: %s\n",__libnvme_msg(fctx->ctx, LIBNVME_LOG_WARN, ((void*)0), "failed to write devid-file %s: %s\n"
, fctx->devid_file, libnvme_strerror((*__errno_location ()
)))
963 fctx->devid_file, libnvme_strerror(errno))__libnvme_msg(fctx->ctx, LIBNVME_LOG_WARN, ((void*)0), "failed to write devid-file %s: %s\n"
, fctx->devid_file, libnvme_strerror((*__errno_location ()
)))
;
964}
965
966__shr_public__attribute__((visibility("default"))) int libnvmf_context_set_io_queues(
967 struct libnvmf_context *fctx, int nr_io_queues,
968 int nr_write_queues, int nr_poll_queues,
969 int queue_size, bool_Bool disable_sqflow)
970{
971 fctx->ctrl_params.cfg.nr_io_queues = nr_io_queues;
972 fctx->ctrl_params.cfg.nr_write_queues = nr_write_queues;
973 fctx->ctrl_params.cfg.nr_poll_queues = nr_poll_queues;
974 fctx->ctrl_params.cfg.queue_size = queue_size;
975 fctx->ctrl_params.cfg.disable_sqflow = disable_sqflow;
976
977 return 0;
978}
979
980__shr_public__attribute__((visibility("default"))) int libnvmf_context_set_reconnect_policy(
981 struct libnvmf_context *fctx, int ctrl_loss_tmo,
982 int reconnect_delay, int fast_io_fail_tmo)
983{
984 fctx->ctrl_params.cfg.ctrl_loss_tmo = ctrl_loss_tmo;
985 fctx->ctrl_params.cfg.reconnect_delay = reconnect_delay;
986 fctx->ctrl_params.cfg.fast_io_fail_tmo = fast_io_fail_tmo;
987
988 return 0;
989}
990
991/*
992 * Derived from Linux's supported options (the opt_tokens table)
993 * when the mechanism to report supported options was added (f18ee3d988157).
994 * Not all of these options may actually be supported,
995 * but we retain the old behavior of passing all that might be.
996 */
997static const struct libnvme_fabric_options default_supported_options = {
998 .ctrl_loss_tmo = true1,
999 .data_digest = true1,
1000 .disable_sqflow = true1,
1001 .discovery = true1,
1002 .duplicate_connect = true1,
1003 .fast_io_fail_tmo = true1,
1004 .hdr_digest = true1,
1005 .host_iface = true1,
1006 .host_traddr = true1,
1007 .hostid = true1,
1008 .hostnqn = true1,
1009 .keep_alive_tmo = true1,
1010 .nqn = true1,
1011 .nr_io_queues = true1,
1012 .nr_poll_queues = true1,
1013 .nr_write_queues = true1,
1014 .queue_size = true1,
1015 .reconnect_delay = true1,
1016 .tos = true1,
1017 .traddr = true1,
1018 .transport = true1,
1019 .trsvcid = true1,
1020};
1021
1022#define MERGE_CFG_OPTION(c, n, o, d)if ((c)->o == d) (c)->o = (n)->o \
1023 if ((c)->o == d) (c)->o = (n)->o
1024static void merge_config(struct libnvme_ctrl *c,
1025 const struct libnvme_fabrics_config *cfg)
1026{
1027 MERGE_CFG_OPTION(&c->cfg, cfg, nr_io_queues, 0)if ((&c->cfg)->nr_io_queues == 0) (&c->cfg)->
nr_io_queues = (cfg)->nr_io_queues
;
1028 MERGE_CFG_OPTION(&c->cfg, cfg, nr_write_queues, 0)if ((&c->cfg)->nr_write_queues == 0) (&c->cfg
)->nr_write_queues = (cfg)->nr_write_queues
;
1029 MERGE_CFG_OPTION(&c->cfg, cfg, nr_poll_queues, 0)if ((&c->cfg)->nr_poll_queues == 0) (&c->cfg
)->nr_poll_queues = (cfg)->nr_poll_queues
;
1030 MERGE_CFG_OPTION(&c->cfg, cfg, queue_size, 0)if ((&c->cfg)->queue_size == 0) (&c->cfg)->
queue_size = (cfg)->queue_size
;
1031 MERGE_CFG_OPTION(&c->cfg, cfg, keep_alive_tmo, 0)if ((&c->cfg)->keep_alive_tmo == 0) (&c->cfg
)->keep_alive_tmo = (cfg)->keep_alive_tmo
;
1032 MERGE_CFG_OPTION(&c->cfg, cfg, reconnect_delay, 0)if ((&c->cfg)->reconnect_delay == 0) (&c->cfg
)->reconnect_delay = (cfg)->reconnect_delay
;
1033 MERGE_CFG_OPTION(&c->cfg, cfg, ctrl_loss_tmo,if ((&c->cfg)->ctrl_loss_tmo == 600) (&c->cfg
)->ctrl_loss_tmo = (cfg)->ctrl_loss_tmo
1034 NVMF_DEF_CTRL_LOSS_TMO)if ((&c->cfg)->ctrl_loss_tmo == 600) (&c->cfg
)->ctrl_loss_tmo = (cfg)->ctrl_loss_tmo
;
1035 MERGE_CFG_OPTION(&c->cfg, cfg, fast_io_fail_tmo, 0)if ((&c->cfg)->fast_io_fail_tmo == 0) (&c->cfg
)->fast_io_fail_tmo = (cfg)->fast_io_fail_tmo
;
1036 MERGE_CFG_OPTION(&c->cfg, cfg, tos, -1)if ((&c->cfg)->tos == -1) (&c->cfg)->tos =
(cfg)->tos
;
1037 MERGE_CFG_OPTION(&c->cfg, cfg, keyring_id, 0)if ((&c->cfg)->keyring_id == 0) (&c->cfg)->
keyring_id = (cfg)->keyring_id
;
1038 MERGE_CFG_OPTION(&c->cfg, cfg, tls_key_id, 0)if ((&c->cfg)->tls_key_id == 0) (&c->cfg)->
tls_key_id = (cfg)->tls_key_id
;
1039 MERGE_CFG_OPTION(&c->cfg, cfg, tls_configured_key_id, 0)if ((&c->cfg)->tls_configured_key_id == 0) (&c->
cfg)->tls_configured_key_id = (cfg)->tls_configured_key_id
;
1040 MERGE_CFG_OPTION(&c->cfg, cfg, duplicate_connect, false)if ((&c->cfg)->duplicate_connect == 0) (&c->
cfg)->duplicate_connect = (cfg)->duplicate_connect
;
1041 MERGE_CFG_OPTION(&c->cfg, cfg, disable_sqflow, false)if ((&c->cfg)->disable_sqflow == 0) (&c->cfg
)->disable_sqflow = (cfg)->disable_sqflow
;
1042 MERGE_CFG_OPTION(&c->cfg, cfg, hdr_digest, false)if ((&c->cfg)->hdr_digest == 0) (&c->cfg)->
hdr_digest = (cfg)->hdr_digest
;
1043 MERGE_CFG_OPTION(&c->cfg, cfg, data_digest, false)if ((&c->cfg)->data_digest == 0) (&c->cfg)->
data_digest = (cfg)->data_digest
;
1044 MERGE_CFG_OPTION(&c->cfg, cfg, tls, false)if ((&c->cfg)->tls == 0) (&c->cfg)->tls =
(cfg)->tls
;
1045 MERGE_CFG_OPTION(&c->cfg, cfg, concat, false)if ((&c->cfg)->concat == 0) (&c->cfg)->concat
= (cfg)->concat
;
1046}
1047
1048#define UPDATE_CFG_OPTION(c, n, o, d)if ((n)->o != d) (c)->o = (n)->o \
1049 if ((n)->o != d) (c)->o = (n)->o
1050static void update_config(struct libnvme_ctrl *c,
1051 const struct libnvme_fabrics_config *cfg)
1052{
1053 UPDATE_CFG_OPTION(&c->cfg, cfg, nr_io_queues, 0)if ((cfg)->nr_io_queues != 0) (&c->cfg)->nr_io_queues
= (cfg)->nr_io_queues
;
1054 UPDATE_CFG_OPTION(&c->cfg, cfg, nr_write_queues, 0)if ((cfg)->nr_write_queues != 0) (&c->cfg)->nr_write_queues
= (cfg)->nr_write_queues
;
1055 UPDATE_CFG_OPTION(&c->cfg, cfg, nr_poll_queues, 0)if ((cfg)->nr_poll_queues != 0) (&c->cfg)->nr_poll_queues
= (cfg)->nr_poll_queues
;
1056 UPDATE_CFG_OPTION(&c->cfg, cfg, queue_size, 0)if ((cfg)->queue_size != 0) (&c->cfg)->queue_size
= (cfg)->queue_size
;
1057 UPDATE_CFG_OPTION(&c->cfg, cfg, keep_alive_tmo, 0)if ((cfg)->keep_alive_tmo != 0) (&c->cfg)->keep_alive_tmo
= (cfg)->keep_alive_tmo
;
1058 UPDATE_CFG_OPTION(&c->cfg, cfg, reconnect_delay, 0)if ((cfg)->reconnect_delay != 0) (&c->cfg)->reconnect_delay
= (cfg)->reconnect_delay
;
1059 UPDATE_CFG_OPTION(&c->cfg, cfg, ctrl_loss_tmo,if ((cfg)->ctrl_loss_tmo != 600) (&c->cfg)->ctrl_loss_tmo
= (cfg)->ctrl_loss_tmo
1060 NVMF_DEF_CTRL_LOSS_TMO)if ((cfg)->ctrl_loss_tmo != 600) (&c->cfg)->ctrl_loss_tmo
= (cfg)->ctrl_loss_tmo
;
1061 UPDATE_CFG_OPTION(&c->cfg, cfg, fast_io_fail_tmo, 0)if ((cfg)->fast_io_fail_tmo != 0) (&c->cfg)->fast_io_fail_tmo
= (cfg)->fast_io_fail_tmo
;
1062 UPDATE_CFG_OPTION(&c->cfg, cfg, tos, -1)if ((cfg)->tos != -1) (&c->cfg)->tos = (cfg)->
tos
;
1063 UPDATE_CFG_OPTION(&c->cfg, cfg, keyring_id, 0)if ((cfg)->keyring_id != 0) (&c->cfg)->keyring_id
= (cfg)->keyring_id
;
1064 UPDATE_CFG_OPTION(&c->cfg, cfg, tls_key_id, 0)if ((cfg)->tls_key_id != 0) (&c->cfg)->tls_key_id
= (cfg)->tls_key_id
;
1065 UPDATE_CFG_OPTION(&c->cfg, cfg, tls_configured_key_id, 0)if ((cfg)->tls_configured_key_id != 0) (&c->cfg)->
tls_configured_key_id = (cfg)->tls_configured_key_id
;
1066 UPDATE_CFG_OPTION(&c->cfg, cfg, duplicate_connect, false)if ((cfg)->duplicate_connect != 0) (&c->cfg)->duplicate_connect
= (cfg)->duplicate_connect
;
1067 UPDATE_CFG_OPTION(&c->cfg, cfg, disable_sqflow, false)if ((cfg)->disable_sqflow != 0) (&c->cfg)->disable_sqflow
= (cfg)->disable_sqflow
;
1068 UPDATE_CFG_OPTION(&c->cfg, cfg, hdr_digest, false)if ((cfg)->hdr_digest != 0) (&c->cfg)->hdr_digest
= (cfg)->hdr_digest
;
1069 UPDATE_CFG_OPTION(&c->cfg, cfg, data_digest, false)if ((cfg)->data_digest != 0) (&c->cfg)->data_digest
= (cfg)->data_digest
;
1070 UPDATE_CFG_OPTION(&c->cfg, cfg, tls, false)if ((cfg)->tls != 0) (&c->cfg)->tls = (cfg)->
tls
;
1071 UPDATE_CFG_OPTION(&c->cfg, cfg, concat, false)if ((cfg)->concat != 0) (&c->cfg)->concat = (cfg
)->concat
;
1072}
1073
1074static int __add_bool_argument(char **argstr, char *tok, bool_Bool arg)
1075{
1076 char *nstr;
1077
1078 if (!arg)
1079 return 0;
1080 if (asprintf(&nstr, "%s,%s", *argstr, tok) < 0) {
1081 return -ENOMEM12;
1082 }
1083 free(*argstr);
1084 *argstr = nstr;
1085
1086 return 0;
1087}
1088
1089static int __add_hex_argument(char **argstr, char *tok, int arg, bool_Bool allow_zero)
1090{
1091 char *nstr;
1092
1093 if (arg < 0 || (!arg && !allow_zero))
1094 return 0;
1095 if (asprintf(&nstr, "%s,%s=0x%08x", *argstr, tok, arg) < 0) {
1096 return -ENOMEM12;
1097 }
1098 free(*argstr);
1099 *argstr = nstr;
1100
1101 return 0;
1102}
1103
1104static int __add_int_argument(char **argstr, char *tok, int arg, bool_Bool allow_zero)
1105{
1106 char *nstr;
1107
1108 if (arg < 0 || (!arg && !allow_zero))
1109 return 0;
1110 if (asprintf(&nstr, "%s,%s=%d", *argstr, tok, arg) < 0) {
1111 return -ENOMEM12;
1112 }
1113 free(*argstr);
1114 *argstr = nstr;
1115
1116 return 0;
1117}
1118
1119static int __add_int_or_minus_one_argument(char **argstr, char *tok, int arg)
1120{
1121 char *nstr;
1122
1123 if (arg < -1)
1124 return 0;
1125 if (asprintf(&nstr, "%s,%s=%d", *argstr, tok, arg) < 0) {
1126 return -ENOMEM12;
1127 }
1128 free(*argstr);
1129 *argstr = nstr;
1130
1131 return 0;
1132}
1133
1134static int __add_argument(char **argstr, const char *tok, const char *arg)
1135{
1136 char *nstr;
1137
1138 if (!arg || arg[0] == '\0' || !strcmp(arg, "none"))
1139 return 0;
1140 if (asprintf(&nstr, "%s,%s=%s", *argstr, tok, arg) < 0) {
1141 return -ENOMEM12;
1142 }
1143 free(*argstr);
1144 *argstr = nstr;
1145
1146 return 0;
1147}
1148
1149static int __nvmf_supported_options(struct libnvme_global_ctx *ctx);
1150#define nvmf_check_option(ctx, tok)({ !__nvmf_supported_options(ctx) && ctx->options->
tok; })
\
1151({ \
1152 !__nvmf_supported_options(ctx) && ctx->options->tok; \
1153})
1154
1155#define add_bool_argument(ctx, argstr, tok, arg)({ int ret; if (({ !__nvmf_supported_options(ctx) && ctx
->options->tok; })) { ret = __add_bool_argument(argstr,
"tok", arg); } else { __libnvme_msg(ctx, LIBNVME_LOG_DEBUG, (
(void*)0), "option \"%s\" ignored\n", "tok"); ret = 0; } ret;
})
\
1156({ \
1157 int ret; \
1158 if (nvmf_check_option(ctx, tok)({ !__nvmf_supported_options(ctx) && ctx->options->
tok; })
) { \
1159 ret = __add_bool_argument(argstr, \
1160 stringify(tok)"tok", \
1161 arg); \
1162 } else { \
1163 libnvme_msg(ctx, LIBNVME_LOG_DEBUG, \__libnvme_msg(ctx, LIBNVME_LOG_DEBUG, ((void*)0), "option \"%s\" ignored\n"
, "tok")
1164 "option \"%s\" ignored\n", \__libnvme_msg(ctx, LIBNVME_LOG_DEBUG, ((void*)0), "option \"%s\" ignored\n"
, "tok")
1165 stringify(tok))__libnvme_msg(ctx, LIBNVME_LOG_DEBUG, ((void*)0), "option \"%s\" ignored\n"
, "tok")
; \
1166 ret = 0; \
1167 } \
1168 ret; \
1169})
1170
1171#define add_hex_argument(ctx, argstr, tok, arg, allow_zero)({ int ret; if (({ !__nvmf_supported_options(ctx) && ctx
->options->tok; })) { ret = __add_hex_argument(argstr, "tok"
, arg, allow_zero); } else { __libnvme_msg(ctx, LIBNVME_LOG_DEBUG
, ((void*)0), "option \"%s\" ignored\n", "tok"); ret = 0; } ret
; })
\
1172({ \
1173 int ret; \
1174 if (nvmf_check_option(ctx, tok)({ !__nvmf_supported_options(ctx) && ctx->options->
tok; })
) { \
1175 ret = __add_hex_argument(argstr, \
1176 stringify(tok)"tok", \
1177 arg, \
1178 allow_zero); \
1179 } else { \
1180 libnvme_msg(ctx, LIBNVME_LOG_DEBUG, \__libnvme_msg(ctx, LIBNVME_LOG_DEBUG, ((void*)0), "option \"%s\" ignored\n"
, "tok")
1181 "option \"%s\" ignored\n", \__libnvme_msg(ctx, LIBNVME_LOG_DEBUG, ((void*)0), "option \"%s\" ignored\n"
, "tok")
1182 stringify(tok))__libnvme_msg(ctx, LIBNVME_LOG_DEBUG, ((void*)0), "option \"%s\" ignored\n"
, "tok")
; \
1183 ret = 0; \
1184 } \
1185 ret; \
1186})
1187
1188#define add_int_argument(ctx, argstr, tok, arg, allow_zero)({ int ret; if (({ !__nvmf_supported_options(ctx) && ctx
->options->tok; })) { ret = __add_int_argument(argstr, "tok"
, arg, allow_zero); } else { __libnvme_msg(ctx, LIBNVME_LOG_DEBUG
, ((void*)0), "option \"%s\" ignored\n", "tok"); ret = 0; } ret
; })
\
1189({ \
1190 int ret; \
1191 if (nvmf_check_option(ctx, tok)({ !__nvmf_supported_options(ctx) && ctx->options->
tok; })
) { \
1192 ret = __add_int_argument(argstr, \
1193 stringify(tok)"tok", \
1194 arg, \
1195 allow_zero); \
1196 } else { \
1197 libnvme_msg(ctx, LIBNVME_LOG_DEBUG, \__libnvme_msg(ctx, LIBNVME_LOG_DEBUG, ((void*)0), "option \"%s\" ignored\n"
, "tok")
1198 "option \"%s\" ignored\n", \__libnvme_msg(ctx, LIBNVME_LOG_DEBUG, ((void*)0), "option \"%s\" ignored\n"
, "tok")
1199 stringify(tok))__libnvme_msg(ctx, LIBNVME_LOG_DEBUG, ((void*)0), "option \"%s\" ignored\n"
, "tok")
; \
1200 ret = 0; \
1201 } \
1202 ret; \
1203})
1204
1205#define add_int_or_minus_one_argument(ctx, argstr, tok, arg)({ int ret; if (({ !__nvmf_supported_options(ctx) && ctx
->options->tok; })) { ret = __add_int_or_minus_one_argument
(argstr, "tok", arg); } else { __libnvme_msg(ctx, LIBNVME_LOG_DEBUG
, ((void*)0), "option \"%s\" ignored\n", "tok"); ret = 0; } ret
; })
\
1206({ \
1207 int ret; \
1208 if (nvmf_check_option(ctx, tok)({ !__nvmf_supported_options(ctx) && ctx->options->
tok; })
) { \
1209 ret = __add_int_or_minus_one_argument(argstr, \
1210 stringify(tok)"tok", \
1211 arg); \
1212 } else { \
1213 libnvme_msg(ctx, LIBNVME_LOG_DEBUG, \__libnvme_msg(ctx, LIBNVME_LOG_DEBUG, ((void*)0), "option \"%s\" ignored\n"
, "tok")
1214 "option \"%s\" ignored\n", \__libnvme_msg(ctx, LIBNVME_LOG_DEBUG, ((void*)0), "option \"%s\" ignored\n"
, "tok")
1215 stringify(tok))__libnvme_msg(ctx, LIBNVME_LOG_DEBUG, ((void*)0), "option \"%s\" ignored\n"
, "tok")
; \
1216 ret = 0; \
1217 } \
1218 ret; \
1219})
1220
1221#define add_argument(ctx, argstr, tok, arg)({ int ret; if (({ !__nvmf_supported_options(ctx) && ctx
->options->tok; })) { ret = __add_argument(argstr, "tok"
, arg); } else { __libnvme_msg(ctx, LIBNVME_LOG_WARN, ((void*
)0), "option \"%s\" ignored\n", "tok"); ret = 0; } ret; })
\
1222({ \
1223 int ret; \
1224 if (nvmf_check_option(ctx, tok)({ !__nvmf_supported_options(ctx) && ctx->options->
tok; })
) { \
1225 ret = __add_argument(argstr, \
1226 stringify(tok)"tok", \
1227 arg); \
1228 } else { \
1229 libnvme_msg(ctx, LIBNVME_LOG_WARN, \__libnvme_msg(ctx, LIBNVME_LOG_WARN, ((void*)0), "option \"%s\" ignored\n"
, "tok")
1230 "option \"%s\" ignored\n", \__libnvme_msg(ctx, LIBNVME_LOG_WARN, ((void*)0), "option \"%s\" ignored\n"
, "tok")
1231 stringify(tok))__libnvme_msg(ctx, LIBNVME_LOG_WARN, ((void*)0), "option \"%s\" ignored\n"
, "tok")
; \
1232 ret = 0; \
1233 } \
1234 ret; \
1235})
1236
1237static int inet4_pton(const char *src, uint16_t port,
1238 struct sockaddr_storage *addr)
1239{
1240 struct sockaddr_in *addr4 = (struct sockaddr_in *)addr;
1241
1242 if (strlen(src) > INET_ADDRSTRLEN16)
1243 return -EINVAL22;
1244
1245 if (inet_pton(AF_INET2, src, &addr4->sin_addr.s_addr) <= 0)
1246 return -EINVAL22;
1247
1248 addr4->sin_family = AF_INET2;
1249 addr4->sin_port = htons(port)__bswap_16 (port);
1250
1251 return 0;
1252}
1253
1254static int inet6_pton(struct libnvme_global_ctx *ctx, const char *src, uint16_t port,
1255 struct sockaddr_storage *addr)
1256{
1257 struct sockaddr_in6 *addr6 = (struct sockaddr_in6 *)addr;
1258 const char *scope = NULL((void*)0);
1259 char *p;
1260
1261 if (strlen(src) > INET6_ADDRSTRLEN46)
1262 return -EINVAL22;
1263
1264 __cleanup_free__attribute__((cleanup(shr_freep))) char *tmp = strdup(src);
1265 if (!tmp) {
1266 libnvme_msg(ctx, LIBNVME_LOG_ERR, "cannot copy: %s\n", src)__libnvme_msg(ctx, LIBNVME_LOG_ERR, ((void*)0), "cannot copy: %s\n"
, src)
;
1267 return -ENOMEM12;
1268 }
1269
1270 p = strchr(tmp, '%')_Generic (0 ? (tmp) : (void *) 1, const void *: (const char *
) (strchr (tmp, '%')), default: strchr (tmp, '%'))
;
1271 if (p) {
1272 *p = '\0';
1273 scope = src + (p - tmp) + 1;
1274 }
1275
1276 if (inet_pton(AF_INET610, tmp, &addr6->sin6_addr) != 1)
1277 return -EINVAL22;
1278
1279 if (IN6_IS_ADDR_LINKLOCAL(&addr6->sin6_addr)(__extension__ ({ const struct in6_addr *__a = (const struct in6_addr
*) (&addr6->sin6_addr); (__a->__in6_u.__u6_addr32[
0] & __bswap_32 (0xffc00000)) == __bswap_32 (0xfe800000);
}))
&& scope) {
1280 addr6->sin6_scope_id = if_nametoindex(scope);
1281 if (addr6->sin6_scope_id == 0) {
1282 libnvme_msg(ctx, LIBNVME_LOG_ERR,__libnvme_msg(ctx, LIBNVME_LOG_ERR, ((void*)0), "can't find iface index for: %s (%m)\n"
, scope)
1283 "can't find iface index for: %s (%m)\n", scope)__libnvme_msg(ctx, LIBNVME_LOG_ERR, ((void*)0), "can't find iface index for: %s (%m)\n"
, scope)
;
1284 return -EINVAL22;
1285 }
1286 }
1287
1288 addr6->sin6_family = AF_INET610;
1289 addr6->sin6_port = htons(port)__bswap_16 (port);
1290 return 0;
1291}
1292
1293/**
1294 * inet_pton_with_scope - convert an IPv4/IPv6 to socket address
1295 * @ctx: Global context
1296 * @af: address family, AF_INET, AF_INET6 or AF_UNSPEC for either
1297 * @src: the start of the address string
1298 * @trsvcid: transport service identifier
1299 * @addr: output socket address
1300 *
1301 * Return 0 on success, errno otherwise.
1302 */
1303static int inet_pton_with_scope(struct libnvme_global_ctx *ctx, int af,
1304 const char *src, const char * trsvcid,
1305 struct sockaddr_storage *addr)
1306{
1307 int ret = -EINVAL22;
1308 uint16_t port = 0;
1309
1310 if (trsvcid) {
1311 unsigned long long tmp = strtoull(trsvcid, NULL((void*)0), 0);
1312 port = (uint16_t)tmp;
1313 if (tmp != port) {
1314 libnvme_msg(ctx, LIBNVME_LOG_ERR, "trsvcid out of range: %s\n",__libnvme_msg(ctx, LIBNVME_LOG_ERR, ((void*)0), "trsvcid out of range: %s\n"
, trsvcid)
1315 trsvcid)__libnvme_msg(ctx, LIBNVME_LOG_ERR, ((void*)0), "trsvcid out of range: %s\n"
, trsvcid)
;
1316 return -ERANGE34;
1317 }
1318 } else {
1319 port = 0;
1320 }
1321
1322 switch (af) {
1323 case AF_INET2:
1324 ret = inet4_pton(src, port, addr);
1325 break;
1326 case AF_INET610:
1327 ret = inet6_pton(ctx, src, port, addr);
1328 break;
1329 case AF_UNSPEC0:
1330 ret = inet4_pton(src, port, addr);
1331 if (ret)
1332 ret = inet6_pton(ctx, src, port, addr);
1333 break;
1334 default:
1335 libnvme_msg(ctx, LIBNVME_LOG_ERR, "unexpected address family %d\n", af)__libnvme_msg(ctx, LIBNVME_LOG_ERR, ((void*)0), "unexpected address family %d\n"
, af)
;
1336 }
1337
1338 return ret;
1339}
1340
1341bool_Bool traddr_is_hostname(struct libnvme_global_ctx *ctx,
1342 const char *transport, const char *traddr)
1343{
1344 if (!traddr || !transport)
1345 return false0;
1346 if (!strcmp(traddr, "none"))
1347 return false0;
1348 if (strcmp(transport, "tcp") && strcmp(transport, "rdma"))
1349 return false0;
1350
1351 return !libnvmf_traddr_is_numeric(traddr);
1352}
1353
1354/*
1355 * Reject a hostname traddr/host_traddr and canonicalize a numeric one,
1356 * routing the check through the TID constructor so the tree keeps one
1357 * definition of acceptable and canonical addressing. Resolving a
1358 * hostname is the caller's job, not libnvme's, so the connect paths
1359 * simply refuse one instead of resolving it.
1360 */
1361static int nvmf_sanitize_addrs(struct libnvme_global_ctx *ctx, struct libnvme_ctrl *c)
1362{
1363 struct libnvmf_tid *tid;
1364 const char *canon;
1365 char *dup;
1366 int rc;
1367
1368 if (traddr_is_hostname(ctx, c->transport, c->traddr)) {
1369 libnvme_msg(ctx, LIBNVME_LOG_ERR,__libnvme_msg(ctx, LIBNVME_LOG_ERR, ((void*)0), "traddr '%s' is not a numeric address; hostname resolution is the caller's responsibility\n"
, c->traddr)
1370 "traddr '%s' is not a numeric address; hostname resolution is the caller's responsibility\n",__libnvme_msg(ctx, LIBNVME_LOG_ERR, ((void*)0), "traddr '%s' is not a numeric address; hostname resolution is the caller's responsibility\n"
, c->traddr)
1371 c->traddr)__libnvme_msg(ctx, LIBNVME_LOG_ERR, ((void*)0), "traddr '%s' is not a numeric address; hostname resolution is the caller's responsibility\n"
, c->traddr)
;
1372 return -ENVME_CONNECT_TRADDR;
1373 }
1374
1375 if (traddr_is_hostname(ctx, c->transport, c->host_traddr)) {
1376 libnvme_msg(ctx, LIBNVME_LOG_ERR,__libnvme_msg(ctx, LIBNVME_LOG_ERR, ((void*)0), "host-traddr '%s' is not a numeric address; hostname resolution is the caller's responsibility\n"
, c->host_traddr)
1377 "host-traddr '%s' is not a numeric address; hostname resolution is the caller's responsibility\n",__libnvme_msg(ctx, LIBNVME_LOG_ERR, ((void*)0), "host-traddr '%s' is not a numeric address; hostname resolution is the caller's responsibility\n"
, c->host_traddr)
1378 c->host_traddr)__libnvme_msg(ctx, LIBNVME_LOG_ERR, ((void*)0), "host-traddr '%s' is not a numeric address; hostname resolution is the caller's responsibility\n"
, c->host_traddr)
;
1379 return -ENVME_CONNECT_TRADDR;
1380 }
1381
1382 rc = libnvmf_tid_from_fields(c->transport, c->traddr, c->trsvcid,
1383 NULL((void*)0), c->host_traddr, c->host_iface, NULL((void*)0), NULL((void*)0), &tid);
1384 if (rc)
1385 return rc;
1386
1387 canon = libnvmf_tid_get_traddr(tid);
1388 if (canon) {
1389 dup = strdup(canon);
1390 if (!dup) {
1391 libnvmf_tid_free(tid);
1392 return -ENOMEM12;
1393 }
1394 free(c->traddr);
1395 c->traddr = dup;
1396 }
1397
1398 canon = libnvmf_tid_get_host_traddr(tid);
1399 if (canon) {
1400 dup = strdup(canon);
1401 if (!dup) {
1402 libnvmf_tid_free(tid);
1403 return -ENOMEM12;
1404 }
1405 free(c->host_traddr);
1406 c->host_traddr = dup;
1407 }
1408
1409 libnvmf_tid_free(tid);
1410
1411 return 0;
1412}
1413
1414static int build_options(struct libnvme_host *h, struct libnvme_ctrl *c, char **argstr)
1415{
1416 const char *transport = libnvme_ctrl_get_transport(c);
1417 const char *hostnqn, *hostid, *hostkey, *ctrlkey = NULL((void*)0);
1418 bool_Bool discover = false0, discovery_nqn = false0;
1419 struct libnvme_global_ctx *ctx = h->ctx;
1420 long keyring_id = 0;
1421 long key_id = 0;
1422 int ret;
1423
1424 if (!transport) {
1425 libnvme_msg(ctx, LIBNVME_LOG_ERR, "need a transport (-t) argument\n")__libnvme_msg(ctx, LIBNVME_LOG_ERR, ((void*)0), "need a transport (-t) argument\n"
)
;
1426 return -ENVME_CONNECT_TARG;
1427 }
1428
1429 if (strncmp(transport, "loop", 4)) {
1430 if (!libnvme_ctrl_get_traddr(c)) {
1431 libnvme_msg(h->ctx, LIBNVME_LOG_ERR, "need a address (-a) argument\n")__libnvme_msg(h->ctx, LIBNVME_LOG_ERR, ((void*)0), "need a address (-a) argument\n"
)
;
1432 return -ENVME_CONNECT_AARG;
1433 }
1434 }
1435
1436 /* always specify nqn as first arg - this will init the string */
1437 if (asprintf(argstr, "nqn=%s",
1438 libnvme_ctrl_get_subsysnqn(c)) < 0) {
1439 return -ENOMEM12;
1440 }
1441
1442 if (!strcmp(libnvme_ctrl_get_subsysnqn(c), NVME_DISC_SUBSYS_NAME"nqn.2014-08.org.nvmexpress.discovery")) {
1443 libnvme_ctrl_set_discovery_ctrl(c, true1);
1444 libnvme_ctrl_set_unique_discovery_ctrl(c, false0);
1445 discovery_nqn = true1;
1446 }
1447
1448 if (libnvme_ctrl_get_discovery_ctrl(c))
1449 discover = true1;
1450
1451 hostnqn = libnvme_host_get_hostnqn(h);
1452 hostid = libnvme_host_get_hostid(h);
1453 hostkey = libnvme_host_get_kxchap_host_key(h);
1454 if (!hostkey)
1455 libnvme_ctrl_get_kxchap_host_key(c, &hostkey, NULL((void*)0));
1456
1457 if (hostkey)
1458 libnvme_ctrl_get_kxchap_ctrl_key(c, &ctrlkey, NULL((void*)0));
1459
1460 if (c->cfg.tls && c->cfg.concat) {
1461 libnvme_msg(h->ctx, LIBNVME_LOG_ERR, "cannot specify --tls and --concat together\n")__libnvme_msg(h->ctx, LIBNVME_LOG_ERR, ((void*)0), "cannot specify --tls and --concat together\n"
)
;
1462 return -ENVME_CONNECT_INVAL;
1463 }
1464
1465 if (c->cfg.concat && !hostkey) {
1466 libnvme_msg(h->ctx, LIBNVME_LOG_ERR, "required argument [--kxchap-secret | -S] not specified with --concat\n")__libnvme_msg(h->ctx, LIBNVME_LOG_ERR, ((void*)0), "required argument [--kxchap-secret | -S] not specified with --concat\n"
)
;
1467 return -ENVME_CONNECT_INVAL;
1468 }
1469
1470 if (c->cfg.tls) {
1471 ret = __libnvmf_import_keys_from_config(h, c,
1472 &keyring_id, &key_id);
1473 if (ret)
1474 return ret;
1475
1476 if (key_id == 0) {
1477 if (c->cfg.tls_configured_key_id)
1478 key_id = c->cfg.tls_configured_key_id;
1479 else
1480 key_id = c->cfg.tls_key_id;
1481 }
1482 }
1483
1484 if (add_argument(ctx, argstr, transport, transport)({ int ret; if (({ !__nvmf_supported_options(ctx) && ctx
->options->transport; })) { ret = __add_argument(argstr
, "transport", transport); } else { __libnvme_msg(ctx, LIBNVME_LOG_WARN
, ((void*)0), "option \"%s\" ignored\n", "transport"); ret = 0
; } ret; })
||
1485 add_argument(ctx, argstr, traddr,({ int ret; if (({ !__nvmf_supported_options(ctx) && ctx
->options->traddr; })) { ret = __add_argument(argstr, "traddr"
, libnvme_ctrl_get_traddr(c)); } else { __libnvme_msg(ctx, LIBNVME_LOG_WARN
, ((void*)0), "option \"%s\" ignored\n", "traddr"); ret = 0; }
ret; })
1486 libnvme_ctrl_get_traddr(c))({ int ret; if (({ !__nvmf_supported_options(ctx) && ctx
->options->traddr; })) { ret = __add_argument(argstr, "traddr"
, libnvme_ctrl_get_traddr(c)); } else { __libnvme_msg(ctx, LIBNVME_LOG_WARN
, ((void*)0), "option \"%s\" ignored\n", "traddr"); ret = 0; }
ret; })
||
1487 add_argument(ctx, argstr, host_traddr,({ int ret; if (({ !__nvmf_supported_options(ctx) && ctx
->options->host_traddr; })) { ret = __add_argument(argstr
, "host_traddr", libnvme_ctrl_get_host_traddr(c)); } else { __libnvme_msg
(ctx, LIBNVME_LOG_WARN, ((void*)0), "option \"%s\" ignored\n"
, "host_traddr"); ret = 0; } ret; })
1488 libnvme_ctrl_get_host_traddr(c))({ int ret; if (({ !__nvmf_supported_options(ctx) && ctx
->options->host_traddr; })) { ret = __add_argument(argstr
, "host_traddr", libnvme_ctrl_get_host_traddr(c)); } else { __libnvme_msg
(ctx, LIBNVME_LOG_WARN, ((void*)0), "option \"%s\" ignored\n"
, "host_traddr"); ret = 0; } ret; })
||
1489 add_argument(ctx, argstr, host_iface,({ int ret; if (({ !__nvmf_supported_options(ctx) && ctx
->options->host_iface; })) { ret = __add_argument(argstr
, "host_iface", libnvme_ctrl_get_host_iface(c)); } else { __libnvme_msg
(ctx, LIBNVME_LOG_WARN, ((void*)0), "option \"%s\" ignored\n"
, "host_iface"); ret = 0; } ret; })
1490 libnvme_ctrl_get_host_iface(c))({ int ret; if (({ !__nvmf_supported_options(ctx) && ctx
->options->host_iface; })) { ret = __add_argument(argstr
, "host_iface", libnvme_ctrl_get_host_iface(c)); } else { __libnvme_msg
(ctx, LIBNVME_LOG_WARN, ((void*)0), "option \"%s\" ignored\n"
, "host_iface"); ret = 0; } ret; })
||
1491 add_argument(ctx, argstr, trsvcid,({ int ret; if (({ !__nvmf_supported_options(ctx) && ctx
->options->trsvcid; })) { ret = __add_argument(argstr, "trsvcid"
, libnvme_ctrl_get_trsvcid(c)); } else { __libnvme_msg(ctx, LIBNVME_LOG_WARN
, ((void*)0), "option \"%s\" ignored\n", "trsvcid"); ret = 0;
} ret; })
1492 libnvme_ctrl_get_trsvcid(c))({ int ret; if (({ !__nvmf_supported_options(ctx) && ctx
->options->trsvcid; })) { ret = __add_argument(argstr, "trsvcid"
, libnvme_ctrl_get_trsvcid(c)); } else { __libnvme_msg(ctx, LIBNVME_LOG_WARN
, ((void*)0), "option \"%s\" ignored\n", "trsvcid"); ret = 0;
} ret; })
||
1493 (hostnqn && add_argument(ctx, argstr, hostnqn, hostnqn)({ int ret; if (({ !__nvmf_supported_options(ctx) && ctx
->options->hostnqn; })) { ret = __add_argument(argstr, "hostnqn"
, hostnqn); } else { __libnvme_msg(ctx, LIBNVME_LOG_WARN, ((void
*)0), "option \"%s\" ignored\n", "hostnqn"); ret = 0; } ret; }
)
) ||
1494 (hostid && add_argument(ctx, argstr, hostid, hostid)({ int ret; if (({ !__nvmf_supported_options(ctx) && ctx
->options->hostid; })) { ret = __add_argument(argstr, "hostid"
, hostid); } else { __libnvme_msg(ctx, LIBNVME_LOG_WARN, ((void
*)0), "option \"%s\" ignored\n", "hostid"); ret = 0; } ret; }
)
) ||
1495 (discover && !discovery_nqn &&
1496 add_bool_argument(ctx, argstr, discovery, true)({ int ret; if (({ !__nvmf_supported_options(ctx) && ctx
->options->discovery; })) { ret = __add_bool_argument(argstr
, "discovery", 1); } else { __libnvme_msg(ctx, LIBNVME_LOG_DEBUG
, ((void*)0), "option \"%s\" ignored\n", "discovery"); ret = 0
; } ret; })
) ||
1497 (hostkey &&
1498 add_argument(ctx, argstr, dhchap_secret, hostkey)({ int ret; if (({ !__nvmf_supported_options(ctx) && ctx
->options->dhchap_secret; })) { ret = __add_argument(argstr
, "dhchap_secret", hostkey); } else { __libnvme_msg(ctx, LIBNVME_LOG_WARN
, ((void*)0), "option \"%s\" ignored\n", "dhchap_secret"); ret
= 0; } ret; })
) ||
1499 (ctrlkey &&
1500 add_argument(ctx, argstr, dhchap_ctrl_secret, ctrlkey)({ int ret; if (({ !__nvmf_supported_options(ctx) && ctx
->options->dhchap_ctrl_secret; })) { ret = __add_argument
(argstr, "dhchap_ctrl_secret", ctrlkey); } else { __libnvme_msg
(ctx, LIBNVME_LOG_WARN, ((void*)0), "option \"%s\" ignored\n"
, "dhchap_ctrl_secret"); ret = 0; } ret; })
) ||
1501 (!discover &&
1502 add_int_argument(ctx, argstr, nr_io_queues,({ int ret; if (({ !__nvmf_supported_options(ctx) && ctx
->options->nr_io_queues; })) { ret = __add_int_argument
(argstr, "nr_io_queues", c->cfg.nr_io_queues, 0); } else {
__libnvme_msg(ctx, LIBNVME_LOG_DEBUG, ((void*)0), "option \"%s\" ignored\n"
, "nr_io_queues"); ret = 0; } ret; })
1503 c->cfg.nr_io_queues, false)({ int ret; if (({ !__nvmf_supported_options(ctx) && ctx
->options->nr_io_queues; })) { ret = __add_int_argument
(argstr, "nr_io_queues", c->cfg.nr_io_queues, 0); } else {
__libnvme_msg(ctx, LIBNVME_LOG_DEBUG, ((void*)0), "option \"%s\" ignored\n"
, "nr_io_queues"); ret = 0; } ret; })
) ||
1504 (!discover &&
1505 add_int_argument(ctx, argstr, nr_write_queues,({ int ret; if (({ !__nvmf_supported_options(ctx) && ctx
->options->nr_write_queues; })) { ret = __add_int_argument
(argstr, "nr_write_queues", c->cfg.nr_write_queues, 0); } else
{ __libnvme_msg(ctx, LIBNVME_LOG_DEBUG, ((void*)0), "option \"%s\" ignored\n"
, "nr_write_queues"); ret = 0; } ret; })
1506 c->cfg.nr_write_queues, false)({ int ret; if (({ !__nvmf_supported_options(ctx) && ctx
->options->nr_write_queues; })) { ret = __add_int_argument
(argstr, "nr_write_queues", c->cfg.nr_write_queues, 0); } else
{ __libnvme_msg(ctx, LIBNVME_LOG_DEBUG, ((void*)0), "option \"%s\" ignored\n"
, "nr_write_queues"); ret = 0; } ret; })
) ||
1507 (!discover &&
1508 add_int_argument(ctx, argstr, nr_poll_queues,({ int ret; if (({ !__nvmf_supported_options(ctx) && ctx
->options->nr_poll_queues; })) { ret = __add_int_argument
(argstr, "nr_poll_queues", c->cfg.nr_poll_queues, 0); } else
{ __libnvme_msg(ctx, LIBNVME_LOG_DEBUG, ((void*)0), "option \"%s\" ignored\n"
, "nr_poll_queues"); ret = 0; } ret; })
1509 c->cfg.nr_poll_queues, false)({ int ret; if (({ !__nvmf_supported_options(ctx) && ctx
->options->nr_poll_queues; })) { ret = __add_int_argument
(argstr, "nr_poll_queues", c->cfg.nr_poll_queues, 0); } else
{ __libnvme_msg(ctx, LIBNVME_LOG_DEBUG, ((void*)0), "option \"%s\" ignored\n"
, "nr_poll_queues"); ret = 0; } ret; })
) ||
1510 (!discover &&
1511 add_int_argument(ctx, argstr, queue_size,({ int ret; if (({ !__nvmf_supported_options(ctx) && ctx
->options->queue_size; })) { ret = __add_int_argument(argstr
, "queue_size", c->cfg.queue_size, 0); } else { __libnvme_msg
(ctx, LIBNVME_LOG_DEBUG, ((void*)0), "option \"%s\" ignored\n"
, "queue_size"); ret = 0; } ret; })
1512 c->cfg.queue_size, false)({ int ret; if (({ !__nvmf_supported_options(ctx) && ctx
->options->queue_size; })) { ret = __add_int_argument(argstr
, "queue_size", c->cfg.queue_size, 0); } else { __libnvme_msg
(ctx, LIBNVME_LOG_DEBUG, ((void*)0), "option \"%s\" ignored\n"
, "queue_size"); ret = 0; } ret; })
) ||
1513 add_int_argument(ctx, argstr, keep_alive_tmo,({ int ret; if (({ !__nvmf_supported_options(ctx) && ctx
->options->keep_alive_tmo; })) { ret = __add_int_argument
(argstr, "keep_alive_tmo", c->cfg.keep_alive_tmo, 0); } else
{ __libnvme_msg(ctx, LIBNVME_LOG_DEBUG, ((void*)0), "option \"%s\" ignored\n"
, "keep_alive_tmo"); ret = 0; } ret; })
1514 c->cfg.keep_alive_tmo, false)({ int ret; if (({ !__nvmf_supported_options(ctx) && ctx
->options->keep_alive_tmo; })) { ret = __add_int_argument
(argstr, "keep_alive_tmo", c->cfg.keep_alive_tmo, 0); } else
{ __libnvme_msg(ctx, LIBNVME_LOG_DEBUG, ((void*)0), "option \"%s\" ignored\n"
, "keep_alive_tmo"); ret = 0; } ret; })
||
1515 add_int_argument(ctx, argstr, reconnect_delay,({ int ret; if (({ !__nvmf_supported_options(ctx) && ctx
->options->reconnect_delay; })) { ret = __add_int_argument
(argstr, "reconnect_delay", c->cfg.reconnect_delay, 0); } else
{ __libnvme_msg(ctx, LIBNVME_LOG_DEBUG, ((void*)0), "option \"%s\" ignored\n"
, "reconnect_delay"); ret = 0; } ret; })
1516 c->cfg.reconnect_delay, false)({ int ret; if (({ !__nvmf_supported_options(ctx) && ctx
->options->reconnect_delay; })) { ret = __add_int_argument
(argstr, "reconnect_delay", c->cfg.reconnect_delay, 0); } else
{ __libnvme_msg(ctx, LIBNVME_LOG_DEBUG, ((void*)0), "option \"%s\" ignored\n"
, "reconnect_delay"); ret = 0; } ret; })
||
1517 (strcmp(transport, "loop") &&
1518 add_int_or_minus_one_argument(ctx, argstr, ctrl_loss_tmo,({ int ret; if (({ !__nvmf_supported_options(ctx) && ctx
->options->ctrl_loss_tmo; })) { ret = __add_int_or_minus_one_argument
(argstr, "ctrl_loss_tmo", c->cfg.ctrl_loss_tmo); } else { __libnvme_msg
(ctx, LIBNVME_LOG_DEBUG, ((void*)0), "option \"%s\" ignored\n"
, "ctrl_loss_tmo"); ret = 0; } ret; })
1519 c->cfg.ctrl_loss_tmo)({ int ret; if (({ !__nvmf_supported_options(ctx) && ctx
->options->ctrl_loss_tmo; })) { ret = __add_int_or_minus_one_argument
(argstr, "ctrl_loss_tmo", c->cfg.ctrl_loss_tmo); } else { __libnvme_msg
(ctx, LIBNVME_LOG_DEBUG, ((void*)0), "option \"%s\" ignored\n"
, "ctrl_loss_tmo"); ret = 0; } ret; })
) ||
1520 (strcmp(transport, "loop") &&
1521 add_int_argument(ctx, argstr, fast_io_fail_tmo,({ int ret; if (({ !__nvmf_supported_options(ctx) && ctx
->options->fast_io_fail_tmo; })) { ret = __add_int_argument
(argstr, "fast_io_fail_tmo", c->cfg.fast_io_fail_tmo, 0); }
else { __libnvme_msg(ctx, LIBNVME_LOG_DEBUG, ((void*)0), "option \"%s\" ignored\n"
, "fast_io_fail_tmo"); ret = 0; } ret; })
1522 c->cfg.fast_io_fail_tmo, false)({ int ret; if (({ !__nvmf_supported_options(ctx) && ctx
->options->fast_io_fail_tmo; })) { ret = __add_int_argument
(argstr, "fast_io_fail_tmo", c->cfg.fast_io_fail_tmo, 0); }
else { __libnvme_msg(ctx, LIBNVME_LOG_DEBUG, ((void*)0), "option \"%s\" ignored\n"
, "fast_io_fail_tmo"); ret = 0; } ret; })
) ||
1523 (strcmp(transport, "loop") &&
1524 add_int_argument(ctx, argstr, tos, c->cfg.tos, true)({ int ret; if (({ !__nvmf_supported_options(ctx) && ctx
->options->tos; })) { ret = __add_int_argument(argstr, "tos"
, c->cfg.tos, 1); } else { __libnvme_msg(ctx, LIBNVME_LOG_DEBUG
, ((void*)0), "option \"%s\" ignored\n", "tos"); ret = 0; } ret
; })
) ||
1525 add_hex_argument(ctx, argstr, keyring, keyring_id, false)({ int ret; if (({ !__nvmf_supported_options(ctx) && ctx
->options->keyring; })) { ret = __add_hex_argument(argstr
, "keyring", keyring_id, 0); } else { __libnvme_msg(ctx, LIBNVME_LOG_DEBUG
, ((void*)0), "option \"%s\" ignored\n", "keyring"); ret = 0;
} ret; })
||
1526 (!strcmp(transport, "tcp") &&
1527 add_hex_argument(ctx, argstr, tls_key, key_id, false)({ int ret; if (({ !__nvmf_supported_options(ctx) && ctx
->options->tls_key; })) { ret = __add_hex_argument(argstr
, "tls_key", key_id, 0); } else { __libnvme_msg(ctx, LIBNVME_LOG_DEBUG
, ((void*)0), "option \"%s\" ignored\n", "tls_key"); ret = 0;
} ret; })
) ||
1528 add_bool_argument(ctx, argstr, duplicate_connect,({ int ret; if (({ !__nvmf_supported_options(ctx) && ctx
->options->duplicate_connect; })) { ret = __add_bool_argument
(argstr, "duplicate_connect", c->cfg.duplicate_connect); }
else { __libnvme_msg(ctx, LIBNVME_LOG_DEBUG, ((void*)0), "option \"%s\" ignored\n"
, "duplicate_connect"); ret = 0; } ret; })
1529 c->cfg.duplicate_connect)({ int ret; if (({ !__nvmf_supported_options(ctx) && ctx
->options->duplicate_connect; })) { ret = __add_bool_argument
(argstr, "duplicate_connect", c->cfg.duplicate_connect); }
else { __libnvme_msg(ctx, LIBNVME_LOG_DEBUG, ((void*)0), "option \"%s\" ignored\n"
, "duplicate_connect"); ret = 0; } ret; })
||
1530 add_bool_argument(ctx, argstr, disable_sqflow,({ int ret; if (({ !__nvmf_supported_options(ctx) && ctx
->options->disable_sqflow; })) { ret = __add_bool_argument
(argstr, "disable_sqflow", c->cfg.disable_sqflow); } else {
__libnvme_msg(ctx, LIBNVME_LOG_DEBUG, ((void*)0), "option \"%s\" ignored\n"
, "disable_sqflow"); ret = 0; } ret; })
1531 c->cfg.disable_sqflow)({ int ret; if (({ !__nvmf_supported_options(ctx) && ctx
->options->disable_sqflow; })) { ret = __add_bool_argument
(argstr, "disable_sqflow", c->cfg.disable_sqflow); } else {
__libnvme_msg(ctx, LIBNVME_LOG_DEBUG, ((void*)0), "option \"%s\" ignored\n"
, "disable_sqflow"); ret = 0; } ret; })
||
1532 (!strcmp(transport, "tcp") &&
1533 add_bool_argument(ctx, argstr, hdr_digest, c->cfg.hdr_digest)({ int ret; if (({ !__nvmf_supported_options(ctx) && ctx
->options->hdr_digest; })) { ret = __add_bool_argument(
argstr, "hdr_digest", c->cfg.hdr_digest); } else { __libnvme_msg
(ctx, LIBNVME_LOG_DEBUG, ((void*)0), "option \"%s\" ignored\n"
, "hdr_digest"); ret = 0; } ret; })
) ||
1534 (!strcmp(transport, "tcp") &&
1535 add_bool_argument(ctx, argstr, data_digest, c->cfg.data_digest)({ int ret; if (({ !__nvmf_supported_options(ctx) && ctx
->options->data_digest; })) { ret = __add_bool_argument
(argstr, "data_digest", c->cfg.data_digest); } else { __libnvme_msg
(ctx, LIBNVME_LOG_DEBUG, ((void*)0), "option \"%s\" ignored\n"
, "data_digest"); ret = 0; } ret; })
) ||
1536 (!strcmp(transport, "tcp") &&
1537 add_bool_argument(ctx, argstr, tls, c->cfg.tls)({ int ret; if (({ !__nvmf_supported_options(ctx) && ctx
->options->tls; })) { ret = __add_bool_argument(argstr,
"tls", c->cfg.tls); } else { __libnvme_msg(ctx, LIBNVME_LOG_DEBUG
, ((void*)0), "option \"%s\" ignored\n", "tls"); ret = 0; } ret
; })
) ||
1538 (!strcmp(transport, "tcp") &&
1539 add_bool_argument(ctx, argstr, concat, c->cfg.concat)({ int ret; if (({ !__nvmf_supported_options(ctx) && ctx
->options->concat; })) { ret = __add_bool_argument(argstr
, "concat", c->cfg.concat); } else { __libnvme_msg(ctx, LIBNVME_LOG_DEBUG
, ((void*)0), "option \"%s\" ignored\n", "concat"); ret = 0; }
ret; })
))
1540 return -1;
1541
1542 return 0;
1543}
1544
1545#define parse_option(ctx, v, name)if (!strcmp(v, "name")) { ctx->options->name = 1; continue
; }
\
1546 if (!strcmp(v, stringify(name)"name")) { \
1547 ctx->options->name = true1; \
1548 continue; \
1549 }
1550
1551static int __nvmf_supported_options(struct libnvme_global_ctx *ctx)
1552{
1553 char buf[0x1000], *options, *p, *v;
1554 __cleanup_fd__attribute__((cleanup(shr_cleanup_fd))) int fd = -1;
1555 ssize_t len;
1556
1557 if (ctx->options)
1558 return 0;
1559
1560 ctx->options = calloc(1, sizeof(*ctx->options));
1561 if (!ctx->options)
1562 return -ENOMEM12;
1563
1564 fd = open(nvmf_dev, O_RDONLY00);
1565 if (fd < 0) {
1566 libnvme_msg(ctx, LIBNVME_LOG_ERR, "Failed to open %s: %s\n",__libnvme_msg(ctx, LIBNVME_LOG_ERR, ((void*)0), "Failed to open %s: %s\n"
, nvmf_dev, libnvme_strerror((*__errno_location ())))
1567 nvmf_dev, libnvme_strerror(errno))__libnvme_msg(ctx, LIBNVME_LOG_ERR, ((void*)0), "Failed to open %s: %s\n"
, nvmf_dev, libnvme_strerror((*__errno_location ())))
;
1568 return -ENVME_CONNECT_OPEN;
1569 }
1570
1571 memset(buf, 0x0, sizeof(buf));
1572 len = read(fd, buf, sizeof(buf) - 1);
1573 if (len < 0) {
1574 if (errno(*__errno_location ()) == EINVAL22) {
1575 /*
1576 * Older Linux kernels don't allow reading from nvmf_dev
1577 * to get supported options, so use a default set
1578 */
1579 libnvme_msg(ctx, LIBNVME_LOG_DEBUG,__libnvme_msg(ctx, LIBNVME_LOG_DEBUG, ((void*)0), "Cannot read %s, using default options\n"
, nvmf_dev)
1580 "Cannot read %s, using default options\n",__libnvme_msg(ctx, LIBNVME_LOG_DEBUG, ((void*)0), "Cannot read %s, using default options\n"
, nvmf_dev)
1581 nvmf_dev)__libnvme_msg(ctx, LIBNVME_LOG_DEBUG, ((void*)0), "Cannot read %s, using default options\n"
, nvmf_dev)
;
1582 *ctx->options = default_supported_options;
1583 return 0;
1584 }
1585
1586 libnvme_msg(ctx, LIBNVME_LOG_ERR, "Failed to read from %s: %s\n",__libnvme_msg(ctx, LIBNVME_LOG_ERR, ((void*)0), "Failed to read from %s: %s\n"
, nvmf_dev, libnvme_strerror((*__errno_location ())))
1587 nvmf_dev, libnvme_strerror(errno))__libnvme_msg(ctx, LIBNVME_LOG_ERR, ((void*)0), "Failed to read from %s: %s\n"
, nvmf_dev, libnvme_strerror((*__errno_location ())))
;
1588 return -ENVME_CONNECT_READ;
1589 }
1590
1591 buf[len] = '\0';
1592 options = buf;
1593
1594 libnvme_msg(ctx, LIBNVME_LOG_DEBUG, "kernel supports: ")__libnvme_msg(ctx, LIBNVME_LOG_DEBUG, ((void*)0), "kernel supports: "
)
;
1595
1596 while ((p = strsep(&options, ",\n")) != NULL((void*)0)) {
1597 if (!*p)
1598 continue;
1599 v = strsep(&p, "= ");
1600 if (!v)
1601 continue;
1602 libnvme_msg(ctx, LIBNVME_LOG_DEBUG, "%s ", v)__libnvme_msg(ctx, LIBNVME_LOG_DEBUG, ((void*)0), "%s ", v);
1603
1604 parse_option(ctx, v, cntlid)if (!strcmp(v, "cntlid")) { ctx->options->cntlid = 1; continue
; }
;
1605 parse_option(ctx, v, concat)if (!strcmp(v, "concat")) { ctx->options->concat = 1; continue
; }
;
1606 parse_option(ctx, v, ctrl_loss_tmo)if (!strcmp(v, "ctrl_loss_tmo")) { ctx->options->ctrl_loss_tmo
= 1; continue; }
;
1607 parse_option(ctx, v, data_digest)if (!strcmp(v, "data_digest")) { ctx->options->data_digest
= 1; continue; }
;
1608 parse_option(ctx, v, dhchap_ctrl_secret)if (!strcmp(v, "dhchap_ctrl_secret")) { ctx->options->dhchap_ctrl_secret
= 1; continue; }
;
1609 parse_option(ctx, v, dhchap_secret)if (!strcmp(v, "dhchap_secret")) { ctx->options->dhchap_secret
= 1; continue; }
;
1610 parse_option(ctx, v, disable_sqflow)if (!strcmp(v, "disable_sqflow")) { ctx->options->disable_sqflow
= 1; continue; }
;
1611 parse_option(ctx, v, discovery)if (!strcmp(v, "discovery")) { ctx->options->discovery =
1; continue; }
;
1612 parse_option(ctx, v, duplicate_connect)if (!strcmp(v, "duplicate_connect")) { ctx->options->duplicate_connect
= 1; continue; }
;
1613 parse_option(ctx, v, fast_io_fail_tmo)if (!strcmp(v, "fast_io_fail_tmo")) { ctx->options->fast_io_fail_tmo
= 1; continue; }
;
1614 parse_option(ctx, v, hdr_digest)if (!strcmp(v, "hdr_digest")) { ctx->options->hdr_digest
= 1; continue; }
;
1615 parse_option(ctx, v, host_iface)if (!strcmp(v, "host_iface")) { ctx->options->host_iface
= 1; continue; }
;
1616 parse_option(ctx, v, host_traddr)if (!strcmp(v, "host_traddr")) { ctx->options->host_traddr
= 1; continue; }
;
1617 parse_option(ctx, v, hostid)if (!strcmp(v, "hostid")) { ctx->options->hostid = 1; continue
; }
;
1618 parse_option(ctx, v, hostnqn)if (!strcmp(v, "hostnqn")) { ctx->options->hostnqn = 1;
continue; }
;
1619 parse_option(ctx, v, instance)if (!strcmp(v, "instance")) { ctx->options->instance = 1
; continue; }
;
1620 parse_option(ctx, v, keep_alive_tmo)if (!strcmp(v, "keep_alive_tmo")) { ctx->options->keep_alive_tmo
= 1; continue; }
;
1621 parse_option(ctx, v, keyring)if (!strcmp(v, "keyring")) { ctx->options->keyring = 1;
continue; }
;
1622 parse_option(ctx, v, nqn)if (!strcmp(v, "nqn")) { ctx->options->nqn = 1; continue
; }
;
1623 parse_option(ctx, v, nr_io_queues)if (!strcmp(v, "nr_io_queues")) { ctx->options->nr_io_queues
= 1; continue; }
;
1624 parse_option(ctx, v, nr_poll_queues)if (!strcmp(v, "nr_poll_queues")) { ctx->options->nr_poll_queues
= 1; continue; }
;
1625 parse_option(ctx, v, nr_write_queues)if (!strcmp(v, "nr_write_queues")) { ctx->options->nr_write_queues
= 1; continue; }
;
1626 parse_option(ctx, v, queue_size)if (!strcmp(v, "queue_size")) { ctx->options->queue_size
= 1; continue; }
;
1627 parse_option(ctx, v, reconnect_delay)if (!strcmp(v, "reconnect_delay")) { ctx->options->reconnect_delay
= 1; continue; }
;
1628 parse_option(ctx, v, tls)if (!strcmp(v, "tls")) { ctx->options->tls = 1; continue
; }
;
1629 parse_option(ctx, v, tls_key)if (!strcmp(v, "tls_key")) { ctx->options->tls_key = 1;
continue; }
;
1630 parse_option(ctx, v, tos)if (!strcmp(v, "tos")) { ctx->options->tos = 1; continue
; }
;
1631 parse_option(ctx, v, traddr)if (!strcmp(v, "traddr")) { ctx->options->traddr = 1; continue
; }
;
1632 parse_option(ctx, v, transport)if (!strcmp(v, "transport")) { ctx->options->transport =
1; continue; }
;
1633 parse_option(ctx, v, trsvcid)if (!strcmp(v, "trsvcid")) { ctx->options->trsvcid = 1;
continue; }
;
1634 }
1635 libnvme_msg(ctx, LIBNVME_LOG_DEBUG, "\n")__libnvme_msg(ctx, LIBNVME_LOG_DEBUG, ((void*)0), "\n");
1636 return 0;
1637}
1638
1639/* Parse the kernel instance number out of a ctrl's name ("nvme3" -> 3). */
1640static int ctrl_instance(struct libnvme_ctrl *c)
1641{
1642 int instance = -1;
1643 const char *name;
1644
1645 if (!c)
1646 return instance;
1647
1648 name = libnvme_ctrl_get_name(c);
1649 if (name)
1650 if (sscanf(name, "nvme%d", &instance) != 1)
1651 instance = -1;
1652
1653 return instance;
1654}
1655
1656enum registry_action {
1657 REG_NONE, /* leave the entry alone */
1658 REG_CLAIM, /* record @owner, overwriting any entry */
1659 REG_CLEAR, /* delete the entry */
1660};
1661
1662/*
1663 * Decide what a successful connect does to the registry entry. Pure
1664 * function, no I/O, so it is directly unit-testable.
1665 *
1666 * @owner: ctx->owner. NULL means --owner was never given, "" means an
1667 * explicit disown, and a name means a claim.
1668 * @fresh: whether the kernel just created this controller for us. A
1669 * controller that already existed may belong to another
1670 * orchestrator, so an unstated intent must not touch its entry.
1671 * A fresh one cannot legitimately be owned by anybody, so its
1672 * entry is cleared to drop what a recycled instance number left
1673 * behind.
1674 */
1675static enum registry_action registry_action_on_connect(const char *owner,
1676 bool_Bool fresh)
1677{
1678 if (owner && *owner)
1679 return REG_CLAIM;
1680
1681 if (fresh || owner)
1682 return REG_CLEAR;
1683
1684 return REG_NONE;
1685}
1686
1687/*
1688 * Best-effort registry update after a successful connect. Failures are
1689 * logged but never fail the connection.
1690 */
1691static void registry_update_on_connect(struct libnvme_global_ctx *ctx,
1692 int instance, bool_Bool fresh)
1693{
1694 int ret;
1695
1696 switch (registry_action_on_connect(ctx->owner, fresh)) {
1697 case REG_CLAIM:
1698 ret = libnvmf_registry_create_instance(ctx, instance,
1699 ctx->owner);
1700 break;
1701 case REG_CLEAR:
1702 ret = libnvmf_registry_delete_instance(ctx, instance);
1703 break;
1704 case REG_NONE:
1705 default:
1706 return;
1707 }
1708
1709 if (ret)
1710 libnvme_msg(ctx, LIBNVME_LOG_WARN,__libnvme_msg(ctx, LIBNVME_LOG_WARN, ((void*)0), "nvme%d: registry update failed: %s\n"
, instance, libnvme_strerror(-ret))
1711 "nvme%d: registry update failed: %s\n",__libnvme_msg(ctx, LIBNVME_LOG_WARN, ((void*)0), "nvme%d: registry update failed: %s\n"
, instance, libnvme_strerror(-ret))
1712 instance, libnvme_strerror(-ret))__libnvme_msg(ctx, LIBNVME_LOG_WARN, ((void*)0), "nvme%d: registry update failed: %s\n"
, instance, libnvme_strerror(-ret))
;
1713}
1714
1715static int __nvmf_add_ctrl(struct libnvme_global_ctx *ctx, const char *argstr)
1716{
1717 __cleanup_fd__attribute__((cleanup(shr_cleanup_fd))) int fd = -1;
1718 int ret, len = strlen(argstr);
1719 int instance;
1720 char buf[0x1000], *options, *p;
1721
1722 fd = open(nvmf_dev, O_RDWR02);
1723 if (fd < 0) {
1724 libnvme_msg(ctx, LIBNVME_LOG_ERR, "Failed to open %s: %s\n",__libnvme_msg(ctx, LIBNVME_LOG_ERR, ((void*)0), "Failed to open %s: %s\n"
, nvmf_dev, libnvme_strerror((*__errno_location ())))
1725 nvmf_dev, libnvme_strerror(errno))__libnvme_msg(ctx, LIBNVME_LOG_ERR, ((void*)0), "Failed to open %s: %s\n"
, nvmf_dev, libnvme_strerror((*__errno_location ())))
;
1726 return -ENVME_CONNECT_OPEN;
1727 }
1728
1729 libnvme_msg(ctx, LIBNVME_LOG_DEBUG, "connect ctrl, '%.*s'\n",__libnvme_msg(ctx, LIBNVME_LOG_DEBUG, ((void*)0), "connect ctrl, '%.*s'\n"
, (int)strcspn(argstr,"\n"), argstr)
1730 (int)strcspn(argstr,"\n"), argstr)__libnvme_msg(ctx, LIBNVME_LOG_DEBUG, ((void*)0), "connect ctrl, '%.*s'\n"
, (int)strcspn(argstr,"\n"), argstr)
;
1731 ret = write(fd, argstr, len);
1732 if (ret != len) {
1733 libnvme_msg(ctx, LIBNVME_LOG_INFO, "Failed to write to %s: %s\n",__libnvme_msg(ctx, LIBNVME_LOG_INFO, ((void*)0), "Failed to write to %s: %s\n"
, nvmf_dev, libnvme_strerror((*__errno_location ())))
1734 nvmf_dev, libnvme_strerror(errno))__libnvme_msg(ctx, LIBNVME_LOG_INFO, ((void*)0), "Failed to write to %s: %s\n"
, nvmf_dev, libnvme_strerror((*__errno_location ())))
;
1735 switch (errno(*__errno_location ())) {
1736 case EALREADY114:
1737 return -ENVME_CONNECT_ALREADY;
1738 case EINVAL22:
1739 return -ENVME_CONNECT_INVAL;
1740 case EADDRINUSE98:
1741 return -ENVME_CONNECT_ADDRINUSE;
1742 case ENODEV19:
1743 return -ENVME_CONNECT_NODEV;
1744 case EOPNOTSUPP95:
1745 return -ENVME_CONNECT_OPNOTSUPP;
1746 case ECONNREFUSED111:
1747 return -ENVME_CONNECT_CONNREFUSED;
1748 case EADDRNOTAVAIL99:
1749 return -ENVME_CONNECT_ADDRNOTAVAIL;
1750 case ENOKEY126:
1751 return -ENVME_CONNECT_NOKEY;
1752 default:
1753 return -ENVME_CONNECT_WRITE;
1754 }
1755 }
1756
1757 memset(buf, 0x0, sizeof(buf));
1758 len = read(fd, buf, sizeof(buf) - 1);
1759 if (len < 0) {
1760 libnvme_msg(ctx, LIBNVME_LOG_ERR, "Failed to read from %s: %s\n",__libnvme_msg(ctx, LIBNVME_LOG_ERR, ((void*)0), "Failed to read from %s: %s\n"
, nvmf_dev, libnvme_strerror((*__errno_location ())))
1761 nvmf_dev, libnvme_strerror(errno))__libnvme_msg(ctx, LIBNVME_LOG_ERR, ((void*)0), "Failed to read from %s: %s\n"
, nvmf_dev, libnvme_strerror((*__errno_location ())))
;
1762 return -ENVME_CONNECT_READ;
1763 }
1764 libnvme_msg(ctx, LIBNVME_LOG_DEBUG, "connect ctrl, response '%.*s'\n",__libnvme_msg(ctx, LIBNVME_LOG_DEBUG, ((void*)0), "connect ctrl, response '%.*s'\n"
, (int)strcspn(buf, "\n"), buf)
1765 (int)strcspn(buf, "\n"), buf)__libnvme_msg(ctx, LIBNVME_LOG_DEBUG, ((void*)0), "connect ctrl, response '%.*s'\n"
, (int)strcspn(buf, "\n"), buf)
;
1766 buf[len] = '\0';
1767 options = buf;
1768 while ((p = strsep(&options, ",\n")) != NULL((void*)0)) {
1769 if (!*p)
1770 continue;
1771 if (sscanf(p, "instance=%d", &instance) == 1) {
1772 registry_update_on_connect(ctx, instance, true1);
1773 return instance;
1774 }
1775 }
1776
1777 libnvme_msg(ctx, LIBNVME_LOG_ERR, "Failed to parse ctrl info for \"%s\"\n", argstr)__libnvme_msg(ctx, LIBNVME_LOG_ERR, ((void*)0), "Failed to parse ctrl info for \"%s\"\n"
, argstr)
;
1778 return -ENVME_CONNECT_PARSE;
1779}
1780
1781
1782static void nvme_parse_tls_args(const char *keyring, const char *tls_key,
1783 const char *tls_key_identity,
1784 struct libnvme_fabrics_config *cfg, struct libnvme_ctrl *c)
1785{
1786 if (keyring) {
1787 char *endptr;
1788 long id = strtol(keyring, &endptr, 0);
1789
1790 if (endptr != keyring)
1791 cfg->keyring_id = id;
1792 else
1793 libnvme_ctrl_set_keyring(c, keyring);
1794 }
1795
1796 if (tls_key_identity)
1797 libnvme_ctrl_set_tls_key_identity(c, tls_key_identity);
1798
1799 if (tls_key) {
1800 char *endptr;
1801 long id = strtol(tls_key, &endptr, 0);
1802
1803 if (endptr != tls_key)
1804 cfg->tls_key_id = id;
1805 else
1806 libnvme_ctrl_set_tls_key(c, tls_key);
1807 }
1808}
1809
1810__shr_public__attribute__((visibility("default"))) int libnvmf_create_ctrl(struct libnvme_global_ctx *ctx,
1811 struct libnvmf_context *fctx, struct libnvme_ctrl **cp)
1812{
1813 struct libnvme_ctrl *c;
1814 int ret;
1815
1816 ret = libnvme_create_ctrl(ctx, &fctx->ctrl_params, &c);
1817 if (ret)
1818 return ret;
1819
1820 /*
1821 * The credentials live on @fctx next to, not inside, ctrl_params,
1822 * so libnvme_create_ctrl() cannot carry them over. Apply them here
1823 * or a controller created from a context that has them would
1824 * silently connect without.
1825 */
1826 if (fctx->hostkey) {
1827 libnvme_ctrl_set_kxchap_host_key(c, fctx->hostkey);
1828 if (fctx->ctrlkey)
1829 libnvme_ctrl_set_kxchap_ctrl_key(c, fctx->ctrlkey);
1830 }
1831
1832 nvme_parse_tls_args(fctx->keyring, fctx->tls_key,
1833 fctx->tls_key_identity, &fctx->ctrl_params.cfg, c);
1834 update_config(c, &fctx->ctrl_params.cfg);
1835
1836 *cp = c;
1837
1838 return 0;
1839}
1840
1841__shr_public__attribute__((visibility("default"))) int libnvmf_add_ctrl(struct libnvme_host *h, struct libnvme_ctrl *c)
1842{
1843 struct libnvme_subsystem *s;
1844 __cleanup_free__attribute__((cleanup(shr_freep))) char *argstr = NULL((void*)0);
1845 int ret;
1846
1847 /* Are duplicate connections allowed on existing controller */
1848 if (libnvme_ctrl_get_name(c) && !c->cfg.duplicate_connect)
1849 return -ENVME_CONNECT_ALREADY;
1850
1851 /* carry over config from an existing ctrl on the same subsystem */
1852 s = libnvme_lookup_subsystem(h, NULL((void*)0), libnvme_ctrl_get_subsysnqn(c));
1853 if (s) {
1854 struct libnvme_ctrl *fc;
1855 struct libnvme_ctrl_params params = {
1856 .transport = libnvme_ctrl_get_transport(c),
1857 .traddr = libnvme_ctrl_get_traddr(c),
1858 .host_traddr = libnvme_ctrl_get_host_traddr(c),
1859 .host_iface = libnvme_ctrl_get_host_iface(c),
1860 .trsvcid = libnvme_ctrl_get_trsvcid(c),
1861 };
1862
1863 fc = libnvme_ctrl_find(s, &params, NULL((void*)0));
1864 if (fc) {
1865 const char *key;
1866
1867 merge_config(c, &fc->cfg);
1868 /*
1869 * An authentication key might already been set
1870 * in @cfg, so ensure to update @c with the correct
1871 * controller key.
1872 */
1873 if (libnvme_ctrl_get_kxchap_host_key(fc, &key,
1874 NULL((void*)0)) == 0)
1875 libnvme_ctrl_set_kxchap_host_key(c, key);
1876 if (libnvme_ctrl_get_kxchap_ctrl_key(fc, &key,
1877 NULL((void*)0)) == 0)
1878 libnvme_ctrl_set_kxchap_ctrl_key(c, key);
1879 if (libnvme_ctrl_get_keyring(fc, &key, NULL((void*)0)) == 0)
1880 libnvme_ctrl_set_keyring(c, key);
1881 key = libnvme_ctrl_get_tls_key_identity(fc);
1882 if (key)
1883 libnvme_ctrl_set_tls_key_identity(c, key);
1884 key = libnvme_ctrl_get_tls_key(fc);
1885 if (key)
1886 libnvme_ctrl_set_tls_key(c, key);
1887 }
1888
1889 }
1890
1891 libnvme_ctrl_set_discovered(c, true1);
1892 ret = nvmf_sanitize_addrs(h->ctx, c);
1893 if (ret)
1894 return ret;
1895
1896 ret = build_options(h, c, &argstr);
1897 if (ret)
1898 return ret;
1899
1900 ret = __nvmf_add_ctrl(h->ctx, argstr);
1901 if (ret < 0)
1902 return ret;
1903
1904 libnvme_msg(h->ctx, LIBNVME_LOG_INFO, "nvme%d: %s connected\n", ret,__libnvme_msg(h->ctx, LIBNVME_LOG_INFO, ((void*)0), "nvme%d: %s connected\n"
, ret, libnvme_ctrl_get_subsysnqn(c))
1905 libnvme_ctrl_get_subsysnqn(c))__libnvme_msg(h->ctx, LIBNVME_LOG_INFO, ((void*)0), "nvme%d: %s connected\n"
, ret, libnvme_ctrl_get_subsysnqn(c))
;
1906 return libnvme_init_ctrl(h, c, ret);
1907}
1908
1909__shr_public__attribute__((visibility("default"))) int libnvmf_connect_ctrl(struct libnvme_ctrl *c)
1910{
1911 __cleanup_free__attribute__((cleanup(shr_freep))) char *argstr = NULL((void*)0);
1912 int ret;
1913
1914 ret = nvmf_sanitize_addrs(c->s->h->ctx, c);
1915 if (ret)
1916 return ret;
1917
1918 ret = build_options(c->s->h, c, &argstr);
1919 if (ret)
1920 return ret;
1921
1922 ret = __nvmf_add_ctrl(c->s->h->ctx, argstr);
1923 if (ret < 0)
1924 return ret;
1925
1926 return 0;
1927}
1928
1929__shr_public__attribute__((visibility("default"))) int libnvmf_disconnect_ctrl(struct libnvme_ctrl *c)
1930{
1931 struct libnvme_global_ctx *ctx = c->s && c->s->h ? c->s->h->ctx : NULL((void*)0);
1932 int ret;
1933
1934 ret = libnvme_set_attr(libnvme_ctrl_get_sysfs_dir(c),
1935 "delete_controller", "1");
1936 if (ret < 0) {
1937 libnvme_msg(ctx, LIBNVME_LOG_ERR,__libnvme_msg(ctx, LIBNVME_LOG_ERR, ((void*)0), "%s: failed to disconnect, error %d\n"
, c->name, (*__errno_location ()))
1938 "%s: failed to disconnect, error %d\n", c->name, errno)__libnvme_msg(ctx, LIBNVME_LOG_ERR, ((void*)0), "%s: failed to disconnect, error %d\n"
, c->name, (*__errno_location ()))
;
1939 return ret;
1940 }
1941 libnvme_msg(ctx, LIBNVME_LOG_INFO, "%s: %s disconnected\n",__libnvme_msg(ctx, LIBNVME_LOG_INFO, ((void*)0), "%s: %s disconnected\n"
, c->name, c->subsysnqn)
1942 c->name, c->subsysnqn)__libnvme_msg(ctx, LIBNVME_LOG_INFO, ((void*)0), "%s: %s disconnected\n"
, c->name, c->subsysnqn)
;
1943 libnvme_deconfigure_ctrl(c);
1944 return 0;
1945}
1946
1947static void nvmf_update_tls_concat(struct nvmf_disc_log_entry *e,
1948 struct libnvme_ctrl *c, struct libnvme_host *h)
1949{
1950 if (!e)
1951 return;
1952
1953 if (e->trtype != NVMF_TRTYPE_TCP ||
1954 e->tsas.tcp.sectype == NVMF_TCP_SECTYPE_NONE)
1955 return;
1956
1957 if (e->treq & NVMF_TREQ_REQUIRED) {
1958 libnvme_msg(h->ctx, LIBNVME_LOG_DEBUG,__libnvme_msg(h->ctx, LIBNVME_LOG_DEBUG, ((void*)0), "setting --tls due to treq %s and sectype %s\n"
, libnvmf_treq_str(e->treq), libnvmf_sectype_str(e->tsas
.tcp.sectype))
1959 "setting --tls due to treq %s and sectype %s\n",__libnvme_msg(h->ctx, LIBNVME_LOG_DEBUG, ((void*)0), "setting --tls due to treq %s and sectype %s\n"
, libnvmf_treq_str(e->treq), libnvmf_sectype_str(e->tsas
.tcp.sectype))
1960 libnvmf_treq_str(e->treq),__libnvme_msg(h->ctx, LIBNVME_LOG_DEBUG, ((void*)0), "setting --tls due to treq %s and sectype %s\n"
, libnvmf_treq_str(e->treq), libnvmf_sectype_str(e->tsas
.tcp.sectype))
1961 libnvmf_sectype_str(e->tsas.tcp.sectype))__libnvme_msg(h->ctx, LIBNVME_LOG_DEBUG, ((void*)0), "setting --tls due to treq %s and sectype %s\n"
, libnvmf_treq_str(e->treq), libnvmf_sectype_str(e->tsas
.tcp.sectype))
;
1962
1963 c->cfg.tls = true1;
1964 return;
1965 }
1966
1967 if (e->treq & NVMF_TREQ_NOT_REQUIRED) {
1968 libnvme_msg(h->ctx, LIBNVME_LOG_DEBUG,__libnvme_msg(h->ctx, LIBNVME_LOG_DEBUG, ((void*)0), "setting --concat due to treq %s and sectype %s\n"
, libnvmf_treq_str(e->treq), libnvmf_sectype_str(e->tsas
.tcp.sectype))
1969 "setting --concat due to treq %s and sectype %s\n",__libnvme_msg(h->ctx, LIBNVME_LOG_DEBUG, ((void*)0), "setting --concat due to treq %s and sectype %s\n"
, libnvmf_treq_str(e->treq), libnvmf_sectype_str(e->tsas
.tcp.sectype))
1970 libnvmf_treq_str(e->treq),__libnvme_msg(h->ctx, LIBNVME_LOG_DEBUG, ((void*)0), "setting --concat due to treq %s and sectype %s\n"
, libnvmf_treq_str(e->treq), libnvmf_sectype_str(e->tsas
.tcp.sectype))
1971 libnvmf_sectype_str(e->tsas.tcp.sectype))__libnvme_msg(h->ctx, LIBNVME_LOG_DEBUG, ((void*)0), "setting --concat due to treq %s and sectype %s\n"
, libnvmf_treq_str(e->treq), libnvmf_sectype_str(e->tsas
.tcp.sectype))
;
1972
1973 c->cfg.concat = true1;
1974 return;
1975 }
1976}
1977
1978/*
1979 * Enumerated-connect gate: consult the exclusion list before connecting a
1980 * controller that was enumerated from a Discovery Log Page, the NBFT table,
1981 * or a configuration file. Controllers named explicitly by the user
1982 * ("nvme connect", "nvme discover" with an address) are deliberately not
1983 * checked -- a targeted human action overrides the list.
1984 */
1985static bool_Bool nvmf_excluded(struct libnvme_global_ctx *ctx,
1986 const char *transport, const char *traddr,
1987 const char *trsvcid, const char *subsysnqn,
1988 const char *host_traddr, const char *host_iface,
1989 const char *hostnqn, const char *hostid)
1990{
1991 struct libnvmf_tid *tid;
1992 bool_Bool excluded;
1993
1994 libnvmf_tid_from_fields(transport, traddr, trsvcid, subsysnqn,
1995 host_traddr, host_iface, hostnqn, hostid, &tid);
1996 if (!tid)
1997 return false0; /* fail-safe: never block on allocation failure */
1998
1999 excluded = libnvmf_exclusion_match(ctx, tid);
2000 if (excluded) {
2001 const char *rendered = libnvmf_tid_str(tid);
2002 libnvme_msg(ctx, LIBNVME_LOG_INFO,__libnvme_msg(ctx, LIBNVME_LOG_INFO, ((void*)0), "skipping excluded controller %s\n"
, rendered ? rendered : subsysnqn)
2003 "skipping excluded controller %s\n",__libnvme_msg(ctx, LIBNVME_LOG_INFO, ((void*)0), "skipping excluded controller %s\n"
, rendered ? rendered : subsysnqn)
2004 rendered ? rendered : subsysnqn)__libnvme_msg(ctx, LIBNVME_LOG_INFO, ((void*)0), "skipping excluded controller %s\n"
, rendered ? rendered : subsysnqn)
;
2005 }
2006 libnvmf_tid_free(tid);
2007
2008 return excluded;
2009}
2010
2011static int nvmf_connect_disc_entry(struct libnvme_host *h,
2012 struct nvmf_disc_log_entry *e,
2013 struct libnvme_ctrl_params *params,
2014 struct libnvme_ctrl **cp)
2015{
2016 struct libnvme_ctrl *c;
2017 int ret;
2018
2019 switch (e->trtype) {
2020 case NVMF_TRTYPE_RDMA:
2021 case NVMF_TRTYPE_TCP:
2022 switch (e->adrfam) {
2023 case NVMF_ADDR_FAMILY_IP4:
2024 case NVMF_ADDR_FAMILY_IP6:
2025 params->traddr = e->traddr;
2026 params->trsvcid = e->trsvcid;
2027 break;
2028 default:
2029 libnvme_msg(h->ctx, LIBNVME_LOG_ERR,__libnvme_msg(h->ctx, LIBNVME_LOG_ERR, ((void*)0), "skipping unsupported adrfam %d\n"
, e->adrfam)
2030 "skipping unsupported adrfam %d\n",__libnvme_msg(h->ctx, LIBNVME_LOG_ERR, ((void*)0), "skipping unsupported adrfam %d\n"
, e->adrfam)
2031 e->adrfam)__libnvme_msg(h->ctx, LIBNVME_LOG_ERR, ((void*)0), "skipping unsupported adrfam %d\n"
, e->adrfam)
;
2032 return -EINVAL22;
2033 }
2034 break;
2035 case NVMF_TRTYPE_FC:
2036 switch (e->adrfam) {
2037 case NVMF_ADDR_FAMILY_FC:
2038 params->traddr = e->traddr;
2039 break;
2040 default:
2041 libnvme_msg(h->ctx, LIBNVME_LOG_ERR,__libnvme_msg(h->ctx, LIBNVME_LOG_ERR, ((void*)0), "skipping unsupported adrfam %d\n"
, e->adrfam)
2042 "skipping unsupported adrfam %d\n",__libnvme_msg(h->ctx, LIBNVME_LOG_ERR, ((void*)0), "skipping unsupported adrfam %d\n"
, e->adrfam)
2043 e->adrfam)__libnvme_msg(h->ctx, LIBNVME_LOG_ERR, ((void*)0), "skipping unsupported adrfam %d\n"
, e->adrfam)
;
2044 return -EINVAL22;
2045 }
2046 break;
2047 case NVMF_TRTYPE_LOOP:
2048 params->traddr = strlen(e->traddr) ? e->traddr : NULL((void*)0);
2049 break;
2050 default:
2051 libnvme_msg(h->ctx, LIBNVME_LOG_ERR, "skipping unsupported transport %d\n",__libnvme_msg(h->ctx, LIBNVME_LOG_ERR, ((void*)0), "skipping unsupported transport %d\n"
, e->trtype)
2052 e->trtype)__libnvme_msg(h->ctx, LIBNVME_LOG_ERR, ((void*)0), "skipping unsupported transport %d\n"
, e->trtype)
;
2053 return -EINVAL22;
2054 }
2055
2056 params->transport = libnvmf_trtype_str(e->trtype);
2057 params->subsysnqn = e->subnqn;
2058
2059 libnvme_msg(h->ctx, LIBNVME_LOG_DEBUG,__libnvme_msg(h->ctx, LIBNVME_LOG_DEBUG, ((void*)0), "lookup ctrl (transport: %s, traddr: %s, trsvcid %s)\n"
, params->transport, params->traddr, params->trsvcid
)
2060 "lookup ctrl (transport: %s, traddr: %s, trsvcid %s)\n",__libnvme_msg(h->ctx, LIBNVME_LOG_DEBUG, ((void*)0), "lookup ctrl (transport: %s, traddr: %s, trsvcid %s)\n"
, params->transport, params->traddr, params->trsvcid
)
2061 params->transport, params->traddr,__libnvme_msg(h->ctx, LIBNVME_LOG_DEBUG, ((void*)0), "lookup ctrl (transport: %s, traddr: %s, trsvcid %s)\n"
, params->transport, params->traddr, params->trsvcid
)
2062 params->trsvcid)__libnvme_msg(h->ctx, LIBNVME_LOG_DEBUG, ((void*)0), "lookup ctrl (transport: %s, traddr: %s, trsvcid %s)\n"
, params->transport, params->traddr, params->trsvcid
)
;
2063
2064 if (nvmf_excluded(h->ctx, params->transport,
2065 params->traddr, params->trsvcid,
2066 params->subsysnqn,
2067 params->host_traddr,
2068 params->host_iface,
2069 libnvme_host_get_hostnqn(h),
2070 libnvme_host_get_hostid(h)))
2071 return -EPERM1;
2072
2073 ret = libnvme_create_ctrl(h->ctx, params, &c);
2074 if (ret) {
2075 libnvme_msg(h->ctx, LIBNVME_LOG_DEBUG, "skipping discovery entry, "__libnvme_msg(h->ctx, LIBNVME_LOG_DEBUG, ((void*)0), "skipping discovery entry, "
"failed to allocate %s controller with traddr %s\n", params->
transport, params->traddr)
2076 "failed to allocate %s controller with traddr %s\n",__libnvme_msg(h->ctx, LIBNVME_LOG_DEBUG, ((void*)0), "skipping discovery entry, "
"failed to allocate %s controller with traddr %s\n", params->
transport, params->traddr)
2077 params->transport, params->traddr)__libnvme_msg(h->ctx, LIBNVME_LOG_DEBUG, ((void*)0), "skipping discovery entry, "
"failed to allocate %s controller with traddr %s\n", params->
transport, params->traddr)
;
2078 return ret;
2079 }
2080
2081 /*
2082 * Self entries (SUBTYPE 03h) are filtered out by the caller before
2083 * this function is ever reached -- they never need a connection of
2084 * their own.
2085 */
2086 switch (e->subtype) {
2087 case NVME_NQN_DISC:
2088 libnvme_ctrl_set_discovery_ctrl(c, true1);
2089 libnvme_ctrl_set_unique_discovery_ctrl(c,
2090 strcmp(e->subnqn, NVME_DISC_SUBSYS_NAME"nqn.2014-08.org.nvmexpress.discovery"));
2091 break;
2092 default:
2093 libnvme_msg(h->ctx, LIBNVME_LOG_ERR, "unsupported subtype %d\n",__libnvme_msg(h->ctx, LIBNVME_LOG_ERR, ((void*)0), "unsupported subtype %d\n"
, e->subtype)
2094 e->subtype)__libnvme_msg(h->ctx, LIBNVME_LOG_ERR, ((void*)0), "unsupported subtype %d\n"
, e->subtype)
;
2095 fallthrough__attribute__((fallthrough));
2096 case NVME_NQN_NVME:
2097 libnvme_ctrl_set_discovery_ctrl(c, false0);
2098 libnvme_ctrl_set_unique_discovery_ctrl(c, false0);
2099 break;
2100 }
2101
2102 if (e->treq & NVMF_TREQ_DISABLE_SQFLOW &&
2103 nvmf_check_option(h->ctx, disable_sqflow)({ !__nvmf_supported_options(h->ctx) && h->ctx->
options->disable_sqflow; })
)
2104 c->cfg.disable_sqflow = true1;
2105
2106 /* update tls or concat */
2107 nvmf_update_tls_concat(e, c, h);
2108
2109 ret = libnvmf_add_ctrl(h, c);
2110 if (!ret) {
2111 *cp = c;
2112 return 0;
2113 }
2114
2115 if (ret == EINVAL22 && c->cfg.disable_sqflow) {
2116 /* disable_sqflow is unrecognized option on older kernels */
2117 libnvme_msg(h->ctx, LIBNVME_LOG_INFO, "failed to connect controller, "__libnvme_msg(h->ctx, LIBNVME_LOG_INFO, ((void*)0), "failed to connect controller, "
"retry with disabling SQ flow control\n")
2118 "retry with disabling SQ flow control\n")__libnvme_msg(h->ctx, LIBNVME_LOG_INFO, ((void*)0), "failed to connect controller, "
"retry with disabling SQ flow control\n")
;
2119 c->cfg.disable_sqflow = false0;
2120 ret = libnvmf_add_ctrl(h, c);
2121 if (!ret) {
2122 *cp = c;
2123 return 0;
2124 }
2125 }
2126 libnvme_free_ctrl(c);
2127 return -ENOENT2;
2128}
2129
2130/*
2131 * Most of nvmf_discovery_log is reserved, so only fetch the initial bytes.
2132 * 8 bytes for GENCTR, 8 for NUMREC, and 2 for RECFMT.
2133 * Since only multiples of 4 bytes are allowed, round 18 up to 20.
2134 */
2135#define DISCOVERY_HEADER_LEN20 20
2136
2137static int nvme_discovery_log(struct libnvme_ctrl *ctrl,
2138 const struct libnvmf_discovery_args *args,
2139 struct nvmf_discovery_log **logp)
2140{
2141 struct libnvme_global_ctx *ctx = ctrl->ctx;
2142 struct nvmf_discovery_log *log;
2143 int retries = 0;
2144 int err;
2145 const char *name = libnvme_ctrl_get_name(ctrl);
2146 uint64_t genctr, numrec;
2147 struct libnvme_transport_handle *hdl;
2148
2149 hdl = libnvme_ctrl_get_transport_handle(ctrl);
2150 struct libnvme_passthru_cmd cmd;
2151
2152 log = libnvme_alloc(sizeof(*log));
2153 if (!log) {
2154 libnvme_msg(ctx, LIBNVME_LOG_ERR,__libnvme_msg(ctx, LIBNVME_LOG_ERR, ((void*)0), "could not allocate memory for discovery log header\n"
)
2155 "could not allocate memory for discovery log header\n")__libnvme_msg(ctx, LIBNVME_LOG_ERR, ((void*)0), "could not allocate memory for discovery log header\n"
)
;
2156 return -ENOMEM12;
2157 }
2158
2159 libnvme_msg(ctx, LIBNVME_LOG_DEBUG, "%s: get header (try %d/%d)\n",__libnvme_msg(ctx, LIBNVME_LOG_DEBUG, ((void*)0), "%s: get header (try %d/%d)\n"
, name, retries, args->max_retries)
2160 name, retries, args->max_retries)__libnvme_msg(ctx, LIBNVME_LOG_DEBUG, ((void*)0), "%s: get header (try %d/%d)\n"
, name, retries, args->max_retries)
;
2161 nvme_init_get_log_discovery(&cmd, 0, log, DISCOVERY_HEADER_LEN20);
2162 err = libnvme_get_log(hdl, &cmd, false0, DISCOVERY_HEADER_LEN20);
2163 if (err) {
2164 libnvme_msg(ctx, LIBNVME_LOG_INFO,__libnvme_msg(ctx, LIBNVME_LOG_INFO, ((void*)0), "%s: discover try %d/%d failed, errno %d status 0x%x\n"
, name, retries, args->max_retries, (*__errno_location ())
, err)
2165 "%s: discover try %d/%d failed, errno %d status 0x%x\n",__libnvme_msg(ctx, LIBNVME_LOG_INFO, ((void*)0), "%s: discover try %d/%d failed, errno %d status 0x%x\n"
, name, retries, args->max_retries, (*__errno_location ())
, err)
2166 name, retries, args->max_retries, errno, err)__libnvme_msg(ctx, LIBNVME_LOG_INFO, ((void*)0), "%s: discover try %d/%d failed, errno %d status 0x%x\n"
, name, retries, args->max_retries, (*__errno_location ())
, err)
;
2167 goto out_free_log;
2168 }
2169
2170 do {
2171 size_t entries_size;
2172
2173 numrec = le64_to_cpu(log->numrec);
2174 genctr = le64_to_cpu(log->genctr);
2175
2176 if (numrec == 0)
2177 break;
2178
2179 if (numrec > (SIZE_MAX(18446744073709551615UL) - sizeof(*log)) / sizeof(*log->entries)) {
2180 libnvme_msg(ctx, LIBNVME_LOG_INFO,__libnvme_msg(ctx, LIBNVME_LOG_INFO, ((void*)0), "%s: don't trust record count %"
"l" "u" "\n", name, numrec)
2181 "%s: don't trust record count %" PRIu64 "\n",__libnvme_msg(ctx, LIBNVME_LOG_INFO, ((void*)0), "%s: don't trust record count %"
"l" "u" "\n", name, numrec)
2182 name, numrec)__libnvme_msg(ctx, LIBNVME_LOG_INFO, ((void*)0), "%s: don't trust record count %"
"l" "u" "\n", name, numrec)
;
2183 err = -EINVAL22;
2184 goto out_free_log;
2185 }
2186
2187 libnvme_free(log);
2188 entries_size = sizeof(*log->entries) * numrec;
2189 log = libnvme_alloc(sizeof(*log) + entries_size);
2190 if (!log) {
2191 libnvme_msg(ctx, LIBNVME_LOG_ERR,__libnvme_msg(ctx, LIBNVME_LOG_ERR, ((void*)0), "could not alloc memory for discovery log page\n"
)
2192 "could not alloc memory for discovery log page\n")__libnvme_msg(ctx, LIBNVME_LOG_ERR, ((void*)0), "could not alloc memory for discovery log page\n"
)
;
2193 return -ENOMEM12;
2194 }
2195
2196 libnvme_msg(ctx, LIBNVME_LOG_DEBUG,__libnvme_msg(ctx, LIBNVME_LOG_DEBUG, ((void*)0), "%s: get %"
"l" "u" " records (genctr %" "l" "u" ")\n", name, numrec, genctr
)
2197 "%s: get %" PRIu64 " records (genctr %" PRIu64 ")\n",__libnvme_msg(ctx, LIBNVME_LOG_DEBUG, ((void*)0), "%s: get %"
"l" "u" " records (genctr %" "l" "u" ")\n", name, numrec, genctr
)
2198 name, numrec, genctr)__libnvme_msg(ctx, LIBNVME_LOG_DEBUG, ((void*)0), "%s: get %"
"l" "u" " records (genctr %" "l" "u" ")\n", name, numrec, genctr
)
;
2199
2200 nvme_init_get_log_discovery(&cmd, sizeof(*log), log->entries, entries_size);
2201 cmd.cdw10 |= NVME_FIELD_ENCODE(args->lsp,(((__u32)(args->lsp) & (NVME_LOG_CDW10_LSP_MASK)) <<
(NVME_LOG_CDW10_LSP_SHIFT))
2202 NVME_LOG_CDW10_LSP_SHIFT,(((__u32)(args->lsp) & (NVME_LOG_CDW10_LSP_MASK)) <<
(NVME_LOG_CDW10_LSP_SHIFT))
2203 NVME_LOG_CDW10_LSP_MASK)(((__u32)(args->lsp) & (NVME_LOG_CDW10_LSP_MASK)) <<
(NVME_LOG_CDW10_LSP_SHIFT))
;
2204 err = libnvme_get_log(hdl, &cmd, false0, NVME_LOG_PAGE_PDU_SIZE4096);
2205 if (err) {
2206 libnvme_msg(ctx, LIBNVME_LOG_INFO,__libnvme_msg(ctx, LIBNVME_LOG_INFO, ((void*)0), "%s: discover try %d/%d failed, errno %d status 0x%x\n"
, name, retries, args->max_retries, (*__errno_location ())
, err)
2207 "%s: discover try %d/%d failed, errno %d status 0x%x\n",__libnvme_msg(ctx, LIBNVME_LOG_INFO, ((void*)0), "%s: discover try %d/%d failed, errno %d status 0x%x\n"
, name, retries, args->max_retries, (*__errno_location ())
, err)
2208 name, retries, args->max_retries, errno, err)__libnvme_msg(ctx, LIBNVME_LOG_INFO, ((void*)0), "%s: discover try %d/%d failed, errno %d status 0x%x\n"
, name, retries, args->max_retries, (*__errno_location ())
, err)
;
2209 goto out_free_log;
2210 }
2211
2212 /*
2213 * If the log page was read with multiple Get Log Page commands,
2214 * genctr must be checked afterwards to ensure atomicity
2215 */
2216 libnvme_msg(ctx, LIBNVME_LOG_DEBUG, "%s: get header again\n", name)__libnvme_msg(ctx, LIBNVME_LOG_DEBUG, ((void*)0), "%s: get header again\n"
, name)
;
2217
2218 nvme_init_get_log_discovery(&cmd, 0, log, DISCOVERY_HEADER_LEN20);
2219 err = libnvme_get_log(hdl, &cmd, false0, DISCOVERY_HEADER_LEN20);
2220 if (err) {
2221 libnvme_msg(ctx, LIBNVME_LOG_INFO,__libnvme_msg(ctx, LIBNVME_LOG_INFO, ((void*)0), "%s: discover try %d/%d failed, errno %d status 0x%x\n"
, name, retries, args->max_retries, (*__errno_location ())
, err)
2222 "%s: discover try %d/%d failed, errno %d status 0x%x\n",__libnvme_msg(ctx, LIBNVME_LOG_INFO, ((void*)0), "%s: discover try %d/%d failed, errno %d status 0x%x\n"
, name, retries, args->max_retries, (*__errno_location ())
, err)
2223 name, retries, args->max_retries, errno, err)__libnvme_msg(ctx, LIBNVME_LOG_INFO, ((void*)0), "%s: discover try %d/%d failed, errno %d status 0x%x\n"
, name, retries, args->max_retries, (*__errno_location ())
, err)
;
2224 goto out_free_log;
2225 }
2226 } while (genctr != le64_to_cpu(log->genctr) &&
2227 ++retries < args->max_retries);
2228
2229 if (genctr != le64_to_cpu(log->genctr)) {
2230 libnvme_msg(ctx, LIBNVME_LOG_INFO, "%s: discover genctr mismatch\n", name)__libnvme_msg(ctx, LIBNVME_LOG_INFO, ((void*)0), "%s: discover genctr mismatch\n"
, name)
;
2231 err = -EAGAIN11;
2232 } else if (numrec != le64_to_cpu(log->numrec)) {
2233 libnvme_msg(ctx, LIBNVME_LOG_INFO,__libnvme_msg(ctx, LIBNVME_LOG_INFO, ((void*)0), "%s: numrec changed unexpectedly "
"from %" "l" "u" " to %" "l" "u" "\n", name, numrec, le64_to_cpu
(log->numrec))
2234 "%s: numrec changed unexpectedly "__libnvme_msg(ctx, LIBNVME_LOG_INFO, ((void*)0), "%s: numrec changed unexpectedly "
"from %" "l" "u" " to %" "l" "u" "\n", name, numrec, le64_to_cpu
(log->numrec))
2235 "from %" PRIu64 " to %" PRIu64 "\n",__libnvme_msg(ctx, LIBNVME_LOG_INFO, ((void*)0), "%s: numrec changed unexpectedly "
"from %" "l" "u" " to %" "l" "u" "\n", name, numrec, le64_to_cpu
(log->numrec))
2236 name, numrec, le64_to_cpu(log->numrec))__libnvme_msg(ctx, LIBNVME_LOG_INFO, ((void*)0), "%s: numrec changed unexpectedly "
"from %" "l" "u" " to %" "l" "u" "\n", name, numrec, le64_to_cpu
(log->numrec))
;
2237 err = -EBADSLT57;
2238 } else {
2239 *logp = log;
2240 return 0;
2241 }
2242
2243out_free_log:
2244 libnvme_free(log);
2245 return err;
2246}
2247
2248static void sanitize_discovery_log_entry(struct libnvme_global_ctx *ctx,
2249 struct nvmf_disc_log_entry *e)
2250{
2251 /*
2252 * Force a NUL terminator into the last byte. Every buffer here is
2253 * far larger than any value a compliant peer can send, so this only
2254 * ever truncates a non-compliant one. The purpose is to keep the
2255 * field from being read as an unbounded char *. This must run
2256 * before shr_rtrim() below, which relies on the field already being
2257 * terminated.
2258 */
2259 e->trsvcid[sizeof(e->trsvcid) - 1] = '\0';
2260 e->subnqn[sizeof(e->subnqn) - 1] = '\0';
2261 e->traddr[sizeof(e->traddr) - 1] = '\0';
2262
2263 shr_rtrim(e->trsvcid);
2264 shr_rtrim(e->traddr);
2265 shr_rtrim(e->subnqn);
2266
2267 /*
2268 * Report traddr always in 'nn-0x:pn-0x' format, but some discovery logs
2269 * provide 'nn-0x,pn-0x'.
2270 */
2271 if (e->trtype == NVMF_TRTYPE_FC) {
2272 char *comma = strchr(e->traddr, ',')_Generic (0 ? (e->traddr) : (void *) 1, const void *: (const
char *) (strchr (e->traddr, ',')), default: strchr (e->
traddr, ','))
;
2273
2274 if (comma) {
2275 libnvme_msg(ctx, LIBNVME_LOG_WARN,__libnvme_msg(ctx, LIBNVME_LOG_WARN, ((void*)0), "invalid traddr separator ',' instead ':', fixing it"
)
2276 "invalid traddr separator ',' instead ':', fixing it")__libnvme_msg(ctx, LIBNVME_LOG_WARN, ((void*)0), "invalid traddr separator ',' instead ':', fixing it"
)
;
2277 *comma = ':';
2278 }
2279 }
2280}
2281
2282__shr_public__attribute__((visibility("default"))) int libnvmf_get_discovery_log(struct libnvme_ctrl *ctrl,
2283 const struct libnvmf_discovery_args *args,
2284 struct nvmf_discovery_log **logp)
2285{
2286 static const struct libnvmf_discovery_args defaults = {
2287 .max_retries = 6,
2288 .lsp = NVMF_LOG_DISC_LSP_NONE,
2289 };
2290 struct nvmf_discovery_log *log;
2291 int err;
2292
2293 if (!args)
2294 args = &defaults;
2295
2296 err = nvme_discovery_log(ctrl, args, &log);
2297 if (err)
2298 return err;
2299
2300 for (int i = 0; i < le64_to_cpu(log->numrec); i++)
2301 sanitize_discovery_log_entry(ctrl->ctx, &log->entries[i]);
2302
2303 *logp = log;
2304 return 0;
2305}
2306
2307
2308/**
2309 * nvmf_get_tel() - Calculate the amount of memory needed for a DIE.
2310 * @hostsymname: Symbolic name (may be NULL)
2311 *
2312 * Each Discovery Information Entry (DIE) must contain at a minimum an
2313 * Extended Attribute for the HostID. The Entry may optionally contain an
2314 * Extended Attribute for the Symbolic Name.
2315 *
2316 * Return: Total Entry Length
2317 */
2318static __u32 nvmf_get_tel(const char *hostsymname)
2319{
2320 __u32 tel = sizeof(struct nvmf_ext_die);
2321 __u16 len;
2322
2323 /* Host ID is mandatory */
2324 tel += libnvmf_exat_size(NVME_UUID_LEN16);
2325
2326 /* Symbolic name is optional */
2327 len = hostsymname ? strlen(hostsymname) : 0;
2328 if (len)
2329 tel += libnvmf_exat_size(len);
2330
2331 return tel;
2332}
2333
2334/**
2335 * nvmf_fill_die() - Fill a Discovery Information Entry.
2336 * @die: Pointer to Discovery Information Entry to be filled
2337 * @h: Pointer to the host data structure
2338 * @tel: Length of the DIE
2339 * @trtype: Transport type
2340 * @adrfam: Address family
2341 * @reg_addr: Address to register. Setting this to an empty string tells
2342 * the DC to infer address from the source address of the socket.
2343 * @tsas: Transport Specific Address Subtype for the address being
2344 * registered.
2345 */
2346static void nvmf_fill_die(struct nvmf_ext_die *die, struct libnvme_host *h,
2347 __u32 tel, __u8 trtype, __u8 adrfam,
2348 const char *reg_addr, union nvmf_tsas *tsas)
2349{
2350 __u16 numexat = 0;
2351 size_t symname_len;
2352 struct nvmf_ext_attr *exat;
2353
2354 die->tel = cpu_to_le32(tel);
2355 die->trtype = trtype;
2356 die->adrfam = adrfam;
2357
2358 memcpy(die->nqn, h->hostnqn, MIN(sizeof(die->nqn), strlen(h->hostnqn))(((sizeof(die->nqn))<(strlen(h->hostnqn)))?(sizeof(die
->nqn)):(strlen(h->hostnqn)))
);
2359 memcpy(die->traddr, reg_addr, MIN(sizeof(die->traddr), strlen(reg_addr))(((sizeof(die->traddr))<(strlen(reg_addr)))?(sizeof(die
->traddr)):(strlen(reg_addr)))
);
2360
2361 if (tsas)
2362 memcpy(&die->tsas, tsas, sizeof(die->tsas));
2363
2364 /* Extended Attribute for the HostID (mandatory) */
2365 numexat++;
2366 exat = die->exat;
2367 exat->exattype = cpu_to_le16(NVMF_EXATTYPE_HOSTID);
2368 exat->exatlen = cpu_to_le16(libnvmf_exat_len(NVME_UUID_LEN16));
2369 libnvme_uuid_from_string(h->hostid, exat->exatval);
2370
2371 /* Extended Attribute for the Symbolic Name (optional) */
2372 symname_len = h->hostsymname ? strlen(h->hostsymname) : 0;
2373 if (symname_len) {
2374 __u16 exatlen = libnvmf_exat_len(symname_len);
2375
2376 numexat++;
2377 exat = libnvmf_exat_ptr_next(exat);
2378 exat->exattype = cpu_to_le16(NVMF_EXATTYPE_SYMNAME);
2379 exat->exatlen = cpu_to_le16(exatlen);
2380 memcpy(exat->exatval, h->hostsymname, symname_len);
2381 /* Per Base specs, ASCII strings must be padded with spaces */
2382 memset(&exat->exatval[symname_len], ' ', exatlen - symname_len);
2383 }
2384
2385 die->numexat = cpu_to_le16(numexat);
2386}
2387
2388/**
2389 * nvmf_dim() - Explicit reg, dereg, reg-update issuing DIM
2390 * @c: Host NVMe controller instance maintaining the admin queue used to
2391 * submit the DIM command to the DC.
2392 * @tas: Task field of the Command Dword 10 (cdw10). Indicates whether to
2393 * perform a Registration, Deregistration, or Registration-update.
2394 * @trtype: Transport type (&enum nvmf_trtype - must be NVMF_TRTYPE_TCP)
2395 * @adrfam: Address family (&enum nvmf_addr_family)
2396 * @reg_addr: Address to register. Setting this to an empty string tells
2397 * the DC to infer address from the source address of the socket.
2398 * @tsas: Transport Specific Address Subtype for the address being
2399 * registered.
2400 * @result: Location where to save the command-specific result returned by
2401 * the discovery controller.
2402 *
2403 * Perform explicit registration, deregistration, or
2404 * registration-update (specified by @tas) by sending a Discovery
2405 * Information Management (DIM) command to the Discovery Controller
2406 * (DC).
2407 *
2408 * Return: 0 on success; on failure -1 is returned and errno is set
2409 */
2410static int nvmf_dim(struct libnvme_ctrl *c, enum nvmf_dim_tas tas, __u8 trtype,
2411 __u8 adrfam, const char *reg_addr, union nvmf_tsas *tsas,
2412 __u32 *result)
2413{
2414 struct libnvme_global_ctx *ctx = c->s && c->s->h ? c->s->h->ctx : NULL((void*)0);
2415 __cleanup_free__attribute__((cleanup(shr_freep))) struct nvmf_dim_data *dim = NULL((void*)0);
2416 struct libnvme_transport_handle *hdl = libnvme_ctrl_get_transport_handle(c);
2417 struct libnvme_passthru_cmd cmd;
2418 struct nvmf_ext_die *die;
2419 __u32 tdl;
2420 __u32 tel;
2421 int ret;
2422
2423 if (!c->s) {
2424 libnvme_msg(ctx, LIBNVME_LOG_ERR,__libnvme_msg(ctx, LIBNVME_LOG_ERR, ((void*)0), "%s: failed to perform DIM. subsystem undefined.\n"
, c->name)
2425 "%s: failed to perform DIM. subsystem undefined.\n",__libnvme_msg(ctx, LIBNVME_LOG_ERR, ((void*)0), "%s: failed to perform DIM. subsystem undefined.\n"
, c->name)
2426 c->name)__libnvme_msg(ctx, LIBNVME_LOG_ERR, ((void*)0), "%s: failed to perform DIM. subsystem undefined.\n"
, c->name)
;
2427 return -EINVAL22;
2428 }
2429
2430 if (!c->s->h) {
2431 libnvme_msg(ctx, LIBNVME_LOG_ERR,__libnvme_msg(ctx, LIBNVME_LOG_ERR, ((void*)0), "%s: failed to perform DIM. host undefined.\n"
, c->name)
2432 "%s: failed to perform DIM. host undefined.\n",__libnvme_msg(ctx, LIBNVME_LOG_ERR, ((void*)0), "%s: failed to perform DIM. host undefined.\n"
, c->name)
2433 c->name)__libnvme_msg(ctx, LIBNVME_LOG_ERR, ((void*)0), "%s: failed to perform DIM. host undefined.\n"
, c->name)
;
2434 return -EINVAL22;
2435 }
2436
2437 if (!c->s->h->hostid) {
2438 libnvme_msg(ctx, LIBNVME_LOG_ERR,__libnvme_msg(ctx, LIBNVME_LOG_ERR, ((void*)0), "%s: failed to perform DIM. hostid undefined.\n"
, c->name)
2439 "%s: failed to perform DIM. hostid undefined.\n",__libnvme_msg(ctx, LIBNVME_LOG_ERR, ((void*)0), "%s: failed to perform DIM. hostid undefined.\n"
, c->name)
2440 c->name)__libnvme_msg(ctx, LIBNVME_LOG_ERR, ((void*)0), "%s: failed to perform DIM. hostid undefined.\n"
, c->name)
;
2441 return -EINVAL22;
2442 }
2443
2444 if (!c->s->h->hostnqn) {
2445 libnvme_msg(ctx, LIBNVME_LOG_ERR,__libnvme_msg(ctx, LIBNVME_LOG_ERR, ((void*)0), "%s: failed to perform DIM. hostnqn undefined.\n"
, c->name)
2446 "%s: failed to perform DIM. hostnqn undefined.\n",__libnvme_msg(ctx, LIBNVME_LOG_ERR, ((void*)0), "%s: failed to perform DIM. hostnqn undefined.\n"
, c->name)
2447 c->name)__libnvme_msg(ctx, LIBNVME_LOG_ERR, ((void*)0), "%s: failed to perform DIM. hostnqn undefined.\n"
, c->name)
;
2448 return -EINVAL22;
2449 }
2450
2451 if (strcmp(c->transport, "tcp")) {
2452 libnvme_msg(ctx, LIBNVME_LOG_ERR,__libnvme_msg(ctx, LIBNVME_LOG_ERR, ((void*)0), "%s: DIM only supported for TCP connections.\n"
, c->name)
2453 "%s: DIM only supported for TCP connections.\n",__libnvme_msg(ctx, LIBNVME_LOG_ERR, ((void*)0), "%s: DIM only supported for TCP connections.\n"
, c->name)
2454 c->name)__libnvme_msg(ctx, LIBNVME_LOG_ERR, ((void*)0), "%s: DIM only supported for TCP connections.\n"
, c->name)
;
2455 return -EINVAL22;
2456 }
2457
2458 /* Register one Discovery Information Entry (DIE) of size TEL */
2459 tel = nvmf_get_tel(c->s->h->hostsymname);
2460 tdl = sizeof(struct nvmf_dim_data) + tel;
2461
2462 dim = (struct nvmf_dim_data *)calloc(1, tdl);
2463 if (!dim) {
2464 return -ENOMEM12;
2465 }
2466
2467 dim->tdl = cpu_to_le32(tdl);
2468 dim->nument = cpu_to_le64(1); /* only one DIE to register */
2469 dim->entfmt = cpu_to_le16(NVMF_DIM_ENTFMT_EXTENDED);
2470 dim->etype = cpu_to_le16(NVMF_DIM_ETYPE_HOST);
2471 dim->ektype = cpu_to_le16(0x5F); /* must be 0x5F per specs */
2472
2473 memcpy(dim->eid, c->s->h->hostnqn,
2474 MIN(sizeof(dim->eid), strlen(c->s->h->hostnqn))(((sizeof(dim->eid))<(strlen(c->s->h->hostnqn)
))?(sizeof(dim->eid)):(strlen(c->s->h->hostnqn)))
);
2475
2476 ret = libnvmf_get_entity_name(dim->ename, sizeof(dim->ename));
2477 if (ret < 0)
2478 libnvme_msg(ctx, LIBNVME_LOG_INFO, "%s: Failed to retrieve ENAME. %s.\n",__libnvme_msg(ctx, LIBNVME_LOG_INFO, ((void*)0), "%s: Failed to retrieve ENAME. %s.\n"
, c->name, libnvme_strerror(-ret))
2479 c->name, libnvme_strerror(-ret))__libnvme_msg(ctx, LIBNVME_LOG_INFO, ((void*)0), "%s: Failed to retrieve ENAME. %s.\n"
, c->name, libnvme_strerror(-ret))
;
2480 else if (ret == 0)
2481 libnvme_msg(ctx, LIBNVME_LOG_INFO, "%s: Failed to retrieve ENAME.\n",__libnvme_msg(ctx, LIBNVME_LOG_INFO, ((void*)0), "%s: Failed to retrieve ENAME.\n"
, c->name)
2482 c->name)__libnvme_msg(ctx, LIBNVME_LOG_INFO, ((void*)0), "%s: Failed to retrieve ENAME.\n"
, c->name)
;
2483
2484 ret = libnvmf_get_entity_version(dim->ever, sizeof(dim->ever));
2485 if (ret <= 0)
2486 libnvme_msg(ctx, LIBNVME_LOG_INFO, "%s: Failed to retrieve EVER.\n", c->name)__libnvme_msg(ctx, LIBNVME_LOG_INFO, ((void*)0), "%s: Failed to retrieve EVER.\n"
, c->name)
;
2487
2488 die = &dim->die->extended;
2489 nvmf_fill_die(die, c->s->h, tel, trtype, adrfam, reg_addr, tsas);
2490
2491 nvme_init_dim_send(&cmd, tas, dim, tdl);
2492 return libnvme_exec_admin_passthru(hdl, &cmd);
2493}
2494
2495/**
2496 * nvme_get_adrfam() - Get address family for the address we're registering
2497 * with the DC.
2498 *
2499 * We retrieve this info from the socket itself. If we can't get the source
2500 * address from the socket, then we'll infer the address family from the
2501 * address of the DC since the DC address has the same address family.
2502 *
2503 * @c: Host NVMe controller instance maintaining the admin queue used to
2504 * submit the DIM command to the DC.
2505 *
2506 * Return: The address family of the source address associated with the
2507 * socket connected to the DC.
2508 */
2509static __u8 nvme_get_adrfam(struct libnvme_ctrl *c)
2510{
2511 struct sockaddr_storage addr;
2512 __u8 adrfam = NVMF_ADDR_FAMILY_IP4;
2513 struct libnvme_global_ctx *ctx = c->s && c->s->h ? c->s->h->ctx : NULL((void*)0);
2514
2515 if (!inet_pton_with_scope(ctx, AF_UNSPEC0, c->traddr, c->trsvcid, &addr)) {
2516 if (addr.ss_family == AF_INET610)
2517 adrfam = NVMF_ADDR_FAMILY_IP6;
2518 }
2519
2520 return adrfam;
2521}
2522
2523/* These string definitions must match with the kernel */
2524static const char *cntrltype_str[] = {
2525 [NVME_CTRL_CNTRLTYPE_IO] = "io",
2526 [NVME_CTRL_CNTRLTYPE_DISCOVERY] = "discovery",
2527 [NVME_CTRL_CNTRLTYPE_ADMIN] = "admin",
2528};
2529
2530static const char *dctype_str[] = {
2531 [NVME_CTRL_DCTYPE_NOT_REPORTED] = "none",
2532 [NVME_CTRL_DCTYPE_DDC] = "ddc",
2533 [NVME_CTRL_DCTYPE_CDC] = "cdc",
2534};
2535
2536/**
2537 * nvme_fetch_cntrltype_dctype_from_id - Get cntrltype and dctype from identify command
2538 * @c: Controller instance
2539 *
2540 * On legacy kernels the cntrltype and dctype are not exposed through the
2541 * sysfs. We must get them directly from the controller by performing an
2542 * identify command.
2543 */
2544static int nvme_fetch_cntrltype_dctype_from_id(struct libnvme_ctrl *c)
2545{
2546 __cleanup_libnvme_free__attribute__((cleanup(libnvme_freep))) struct nvme_id_ctrl *id = NULL((void*)0);
2547 const char *val;
2548 int ret;
2549
2550 id = libnvme_alloc(sizeof(*id));
2551 if (!id)
2552 return -ENOMEM12;
2553
2554 ret = libnvme_ctrl_identify(c, id);
2555 if (ret)
2556 return ret;
2557
2558 if (libnvme_ctrl_get_cntrltype(c, &val, NULL((void*)0))) {
2559 if (id->cntrltype > NVME_CTRL_CNTRLTYPE_ADMIN || !cntrltype_str[id->cntrltype])
2560 libnvme_ctrl_set_cntrltype(c, "reserved");
2561 else
2562 libnvme_ctrl_set_cntrltype(c,
2563 cntrltype_str[id->cntrltype]);
2564 }
2565
2566 if (libnvme_ctrl_get_dctype(c, &val, NULL((void*)0))) {
2567 if (id->dctype > NVME_CTRL_DCTYPE_CDC || !dctype_str[id->dctype])
2568 libnvme_ctrl_set_dctype(c, "reserved");
2569 else
2570 libnvme_ctrl_set_dctype(c, dctype_str[id->dctype]);
2571 }
2572 return 0;
2573}
2574
2575__shr_public__attribute__((visibility("default"))) bool_Bool libnvmf_is_registration_supported(struct libnvme_ctrl *c)
2576{
2577 const char *cntrltype, *dctype;
2578
2579 if (libnvme_ctrl_get_cntrltype(c, &cntrltype, NULL((void*)0)) ||
2580 libnvme_ctrl_get_dctype(c, &dctype, NULL((void*)0)))
2581 if (nvme_fetch_cntrltype_dctype_from_id(c))
2582 return false0;
2583
2584 libnvme_ctrl_get_dctype(c, &dctype, NULL((void*)0));
2585 return shr_streq0(dctype, "ddc") || shr_streq0(dctype, "cdc");
2586}
2587
2588__shr_public__attribute__((visibility("default"))) int libnvmf_register_ctrl(
2589 struct libnvme_ctrl *c, enum nvmf_dim_tas tas, __u32 *result)
2590{
2591 if (!libnvmf_is_registration_supported(c))
2592 return -ENOTSUP95;
2593
2594 /* We're registering our source address with the DC. To do
2595 * that, we can simply send an empty string. This tells the DC
2596 * to retrieve the source address from the socket and use that
2597 * as the registration address.
2598 */
2599 return nvmf_dim(c, tas, NVMF_TRTYPE_TCP, nvme_get_adrfam(c), "", NULL((void*)0), result);
2600}
2601
2602#define IS_XDIGIT(c)((c >= '0' && c <= '9') || (c >= 'A' &&
c <= 'F') || (c >= 'a' && c <= 'f'))
((c >= '0' && c <= '9') || \
2603 (c >= 'A' && c <= 'F') || \
2604 (c >= 'a' && c <= 'f'))
2605#define XDIGIT_VAL(c)((c >= '0' && c <= '9') ? c - '0' : ( (c >= 'A'
&& c <= 'F') ? c - 'A' + 10 : c - 'a' + 10))
((c >= '0' && c <= '9') ? c - '0' : ( \
2606 (c >= 'A' && c <= 'F') ? c - 'A' + 10 : c - 'a' + 10))
2607
2608/* returns newly allocated string */
2609static char *unescape_uri(const char *str, int len)
2610{
2611 char *dst;
2612 int l;
2613 int i, j;
2614
2615 l = len > 0 ? len : strlen(str);
2616 dst = malloc(l + 1);
2617 for (i = 0, j = 0; i < l; i++, j++) {
2618 if (str[i] == '%' && i + 2 < l &&
2619 IS_XDIGIT(str[i + 1])((str[i + 1] >= '0' && str[i + 1] <= '9') || (str
[i + 1] >= 'A' && str[i + 1] <= 'F') || (str[i +
1] >= 'a' && str[i + 1] <= 'f'))
&& IS_XDIGIT(str[i + 2])((str[i + 2] >= '0' && str[i + 2] <= '9') || (str
[i + 2] >= 'A' && str[i + 2] <= 'F') || (str[i +
2] >= 'a' && str[i + 2] <= 'f'))
) {
2620 dst[j] = (XDIGIT_VAL(str[i + 1])((str[i + 1] >= '0' && str[i + 1] <= '9') ? str
[i + 1] - '0' : ( (str[i + 1] >= 'A' && str[i + 1]
<= 'F') ? str[i + 1] - 'A' + 10 : str[i + 1] - 'a' + 10))
<< 4) +
2621 XDIGIT_VAL(str[i + 2])((str[i + 2] >= '0' && str[i + 2] <= '9') ? str
[i + 2] - '0' : ( (str[i + 2] >= 'A' && str[i + 2]
<= 'F') ? str[i + 2] - 'A' + 10 : str[i + 2] - 'a' + 10))
;
2622 i += 2;
2623 } else
2624 dst[j] = str[i];
2625 }
2626 dst[j] = '\0';
2627 return dst;
2628}
2629
2630__shr_public__attribute__((visibility("default"))) int libnvmf_uri_parse(
2631 const char *str, struct libnvmf_uri **urip)
2632{
2633 __cleanup_uri__attribute__((cleanup(free_uri))) struct libnvmf_uri *uri = NULL((void*)0);
2634 __cleanup_free__attribute__((cleanup(shr_freep))) char *scheme = NULL((void*)0);
2635 __cleanup_free__attribute__((cleanup(shr_freep))) char *authority = NULL((void*)0);
2636 __cleanup_free__attribute__((cleanup(shr_freep))) char *path = NULL((void*)0);
2637 __cleanup_free__attribute__((cleanup(shr_freep))) char *h = NULL((void*)0);
2638 const char *host;
2639 int i;
2640
2641 /* As defined in Boot Specification rev. 1.0:
2642 *
2643 * section 1.5.7: NVMe-oF URI Format
2644 * nvme+tcp://192.168.1.1:4420/
2645 * nvme+tcp://[FE80::1010]:4420/
2646 *
2647 * section 3.1.2.5.3: DHCP Root-Path - a hierarchical NVMe-oF URI Format
2648 * NVME<+PROTOCOL>://<SERVERNAME/IP>[:TRANSPORT PORT]/<SUBSYS NQN>/<NID>
2649 * or
2650 * NVME<+PROTOCOL>://<DISCOVERY CONTROLLER ADDRESS>[:DISCOVERY-
2651 * -CONTROLLER PORT]/NQN.2014-08.ORG.NVMEXPRESS.DISCOVERY/<NID>
2652 */
2653
2654 uri = calloc(1, sizeof(struct libnvmf_uri));
2655 if (!uri)
2656 return -ENOMEM12;
2657
2658 if (sscanf(str, "%m[^:/]://%m[^/?#]%ms",
2659 &scheme, &authority, &path) < 2)
2660 return -EINVAL22;
2661
2662 if (sscanf(scheme, "%m[^+]+%ms",
2663 &uri->scheme, &uri->protocol) < 1)
2664 return -EINVAL22;
2665
2666 /*
2667 * The scheme and transport protocol are a fixed, small vocabulary
2668 * ("nvme", "tcp"/"rdma"/"fc"), unlike the host/subsystem NQNs that can
2669 * also appear in this URI -- so, unlike those, normalizing case here
2670 * costs nothing. Boot Specification section 3.1.2.5.3 spells the DHCP
2671 * root-path form in upper case ("NVME<+PROTOCOL>://..."), and both that
2672 * and mixed case are otherwise rejected by validate_uri()'s
2673 * case-sensitive comparisons below.
2674 */
2675 shr_strtolower(uri->scheme);
2676 if (uri->protocol)
2677 shr_strtolower(uri->protocol);
2678
2679 /* split userinfo */
2680 host = strrchr(authority, '@')_Generic (0 ? (authority) : (void *) 1, const void *: (const char
*) (strrchr (authority, '@')), default: strrchr (authority, '@'
))
;
2681 if (host) {
2682 host++;
2683 uri->userinfo = unescape_uri(authority, host - authority);
2684 } else
2685 host = authority;
2686
2687 /* try matching IPv6 address first */
2688 if (sscanf(host, "[%m[^]]]:%d",
2689 &uri->host, &uri->port) < 1) {
2690 /* treat it as IPv4/hostname */
2691 if (sscanf(host, "%m[^:]:%d",
2692 &h, &uri->port) < 1)
2693 return -EINVAL22;
2694 uri->host = unescape_uri(h, 0);
2695 }
2696
2697 /* split path into elements */
2698 if (path) {
2699 char *e, *elem;
2700
2701 /* separate the fragment */
2702 e = strrchr(path, '#')_Generic (0 ? (path) : (void *) 1, const void *: (const char *
) (strrchr (path, '#')), default: strrchr (path, '#'))
;
2703 if (e) {
2704 uri->fragment = unescape_uri(e + 1, 0);
2705 *e = '\0';
2706 }
2707 /* separate the query string */
2708 e = strrchr(path, '?')_Generic (0 ? (path) : (void *) 1, const void *: (const char *
) (strrchr (path, '?')), default: strrchr (path, '?'))
;
2709 if (e) {
2710 uri->query = unescape_uri(e + 1, 0);
2711 *e = '\0';
2712 }
2713
2714 /* count elements first */
2715 for (i = 0, e = path; *e; e++)
2716 if (*e == '/' && *(e + 1) != '/')
2717 i++;
2718 uri->path_segments = calloc(i + 2, sizeof(char *));
2719 if (!uri->path_segments)
2720 return -ENOMEM12;
2721
2722 /*
2723 * libnvmf_uri_free() walks path_segments until the first NULL
2724 * entry, so a failed unescape_uri() partway through must abort
2725 * the whole parse instead of leaving a NULL hole followed by
2726 * more entries that would then never be freed.
2727 */
2728 i = 0;
2729 elem = strtok_r(path, "/", &e);
2730 if (elem) {
2731 uri->path_segments[i] = unescape_uri(elem, 0);
2732 if (!uri->path_segments[i])
2733 return -ENOMEM12;
2734 i++;
2735 }
2736 while (elem && strlen(elem)) {
2737 elem = strtok_r(NULL((void*)0), "/", &e);
2738 if (elem) {
2739 uri->path_segments[i] = unescape_uri(elem, 0);
2740 if (!uri->path_segments[i])
2741 return -ENOMEM12;
2742 i++;
2743 }
2744 }
2745 }
2746
2747 *urip = uri;
2748 uri = NULL((void*)0);
2749
2750 return 0;
2751}
2752
2753__shr_public__attribute__((visibility("default"))) void libnvmf_uri_free(struct libnvmf_uri *uri)
2754{
2755 char **s;
2756
2757 if (!uri)
2758 return;
2759 free(uri->scheme);
2760 free(uri->protocol);
2761 free(uri->userinfo);
2762 free(uri->host);
2763 for (s = uri->path_segments; s && *s; s++)
2764 free(*s);
2765 free(uri->path_segments);
2766 free(uri->query);
2767 free(uri->fragment);
2768 free(uri);
2769}
2770
2771static struct libnvme_ctrl *lookup_ctrl(struct libnvme_host *h,
2772 const struct libnvme_ctrl_params *params)
2773{
2774 struct libnvme_subsystem *s;
2775 struct libnvme_ctrl *c;
2776
2777 libnvme_for_each_subsystem(h, s)for (s = libnvme_first_subsystem(h); s != ((void*)0); s = libnvme_next_subsystem
(h, s))
{
2778 c = libnvme_ctrl_find(s, params, NULL((void*)0));
2779 if (c)
2780 return c;
2781 }
2782
2783 return NULL((void*)0);
2784}
2785
2786/*
2787 * Same as lookup_ctrl(), but only returns a controller that's actually
2788 * connected. lookup_ctrl() can also match a scanned-but-unconnected
2789 * draft, which has no kernel-assigned name yet.
2790 */
2791static struct libnvme_ctrl *lookup_live_ctrl(struct libnvme_host *h,
2792 const struct libnvme_ctrl_params *params)
2793{
2794 struct libnvme_ctrl *c = lookup_ctrl(h, params);
2795
2796 return (c && libnvme_ctrl_get_name(c)) ? c : NULL((void*)0);
2797}
2798
2799__shr_public__attribute__((visibility("default"))) int libnvmf_get_owner_from_tid(struct libnvme_global_ctx *ctx,
2800 const struct libnvmf_tid *tid, char **owner)
2801{
2802 struct libnvme_ctrl_params params = { 0 };
2803 struct libnvme_subsystem *s;
2804 struct libnvme_host *h;
2805 struct libnvme_ctrl *c = NULL((void*)0);
2806 int ret;
2807
2808 if (!ctx || !tid || !owner)
2809 return -EINVAL22;
2810
2811 *owner = NULL((void*)0);
2812
2813 h = libnvme_lookup_host(ctx, libnvmf_tid_get_hostnqn(tid),
2814 libnvmf_tid_get_hostid(tid));
2815 if (!h)
2816 return 0;
2817
2818 params.transport = libnvmf_tid_get_transport(tid);
2819 params.traddr = libnvmf_tid_get_traddr(tid);
2820 params.trsvcid = libnvmf_tid_get_trsvcid(tid);
2821 params.subsysnqn = libnvmf_tid_get_subsysnqn(tid);
2822 params.host_traddr = libnvmf_tid_get_host_traddr(tid);
2823 params.host_iface = libnvmf_tid_get_host_iface(tid);
2824
2825 libnvme_for_each_subsystem(h, s)for (s = libnvme_first_subsystem(h); s != ((void*)0); s = libnvme_next_subsystem
(h, s))
{
2826 c = libnvme_ctrl_find(s, &params, NULL((void*)0));
2827 if (c)
2828 break;
2829 }
2830 if (!c)
2831 return 0;
2832
2833 ret = libnvmf_registry_retrieve(ctx, libnvme_ctrl_get_name(c),
2834 "owner", owner);
2835 return (ret == -ENOENT2) ? 0 : ret;
2836}
2837
2838__shr_public__attribute__((visibility("default"))) int libnvmf_get_owner_from_fctx(struct libnvme_global_ctx *ctx,
2839 struct libnvmf_context *fctx, char **owner)
2840{
2841 struct libnvmf_tid *tid;
2842 const char *name;
2843 int ret;
2844
2845 if (!ctx || !fctx || !owner)
2846 return -EINVAL22;
2847
2848 *owner = NULL((void*)0);
2849
2850 name = libnvmf_context_get_device(fctx);
2851 if (name) {
2852 ret = libnvmf_registry_retrieve(ctx, name, "owner", owner);
2853 return (ret == -ENOENT2) ? 0 : ret;
2854 }
2855
2856 ret = libnvmf_tid_from_fields(
2857 libnvmf_context_get_transport(fctx),
2858 libnvmf_context_get_traddr(fctx),
2859 libnvmf_context_get_trsvcid(fctx),
2860 libnvmf_context_get_subsysnqn(fctx),
2861 libnvmf_context_get_host_traddr(fctx),
2862 libnvmf_context_get_host_iface(fctx),
2863 libnvmf_context_get_hostnqn(fctx),
2864 libnvmf_context_get_hostid(fctx), &tid);
2865 if (ret)
2866 return ret;
2867
2868 ret = libnvmf_get_owner_from_tid(ctx, tid, owner);
2869 libnvmf_tid_free(tid);
2870
2871 return ret;
2872}
2873
2874static int setup_connection(struct libnvmf_context *fctx, struct libnvme_host *h,
2875 bool_Bool discovery)
2876{
2877 if (fctx->hostkey)
2878 libnvme_host_set_kxchap_host_key(h, fctx->hostkey);
2879
2880 if (!fctx->ctrl_params.trsvcid)
2881 fctx->ctrl_params.trsvcid =
2882 libnvmf_get_default_trsvcid(fctx->ctrl_params.transport,
2883 discovery);
2884
2885 return 0;
2886}
2887
2888
2889static int set_discovery_kato(struct libnvmf_context *fctx,
2890 struct libnvme_ctrl_params *params)
2891{
2892 int tmo = params->cfg.keep_alive_tmo;
2893 /*
2894 * EPCSD isn't known until after the Discovery Log Page comes back, so
2895 * auto mode optimistically requests a KATO here; dc_decide()
2896 * disconnects per entry afterward if EPCSD turns out to be unset.
2897 */
2898 bool_Bool wants_kato = fctx->persistent == LIBNVMF_PERSISTENT_AUTO ||
2899 fctx->persistent == LIBNVMF_PERSISTENT_FORCE;
2900
2901 /* Set kato to NVMF_DEF_DISC_TMO for persistent controllers */
2902 if (wants_kato && !params->cfg.keep_alive_tmo)
2903 params->cfg.keep_alive_tmo = fctx->default_keep_alive_timeout;
2904 /* Set kato to zero for non-persistent controllers */
2905 else if (!wants_kato && params->cfg.keep_alive_tmo > 0)
2906 params->cfg.keep_alive_tmo = 0;
2907
2908 return tmo;
2909}
2910
2911enum dc_ownership {
2912 DC_OWNED,
2913 DC_BORROWED,
2914};
2915
2916/*
2917 * Decide whether a discovery controller connection should be disconnected
2918 * once its own Discovery Log Page has been fully walked. Pure function,
2919 * no I/O, so it is directly unit-testable.
2920 *
2921 * @own: DC_BORROWED means this connection pre-existed the walk and is
2922 * never ours to disconnect, regardless of persistence mode.
2923 * @self_seen: whether this DC's own self entry (SUBTYPE 03h, "current
2924 * discovery subsystem") was found in its own Discovery Log
2925 * Page.
2926 * @self_eflags: that self entry's EFLAGS; meaningful only if @self_seen.
2927 * @parent_eflags: the EFLAGS this DC's own referral entry carried in its
2928 * parent's Discovery Log Page, or NULL if this DC has no
2929 * parent (the primary). Used only as a fallback when
2930 * @self_seen is false.
2931 */
2932static bool_Bool dc_decide(struct libnvmf_context *fctx, const char *subnqn,
2933 enum dc_ownership own, bool_Bool self_seen, __u16 self_eflags,
2934 const __u16 *parent_eflags)
2935{
2936 __u16 eflags;
2937 bool_Bool disconnect;
2938
2939 if (own == DC_BORROWED)
2940 return false0;
2941
2942 if (self_seen)
2943 eflags = self_eflags;
2944 else if (parent_eflags)
2945 eflags = *parent_eflags;
2946 else
2947 eflags = 0;
2948
2949 switch (fctx->persistent) {
2950 case LIBNVMF_PERSISTENT_FORCE:
2951 /* Persist regardless of what EPCSD reports. */
2952 disconnect = false0;
2953 break;
2954 case LIBNVMF_PERSISTENT_AUTO:
2955 /* Persist only where the entry's own EPCSD flag says so. */
2956 disconnect = !(eflags & NVMF_DISC_EFLAGS_EPCSD);
2957 if (disconnect)
2958 libnvme_msg(fctx->ctx, LIBNVME_LOG_WARN,__libnvme_msg(fctx->ctx, LIBNVME_LOG_WARN, ((void*)0), "%s: not persisting, EPCSD=0\n"
, subnqn)
2959 "%s: not persisting, EPCSD=0\n", subnqn)__libnvme_msg(fctx->ctx, LIBNVME_LOG_WARN, ((void*)0), "%s: not persisting, EPCSD=0\n"
, subnqn)
;
2960 break;
2961 case LIBNVMF_PERSISTENT_NO:
2962 case LIBNVMF_PERSISTENT_UNSET:
2963 default:
2964 disconnect = true1;
2965 break;
2966 }
2967
2968 return disconnect;
2969}
2970
2971/*
2972 * Bundles the controller and the decisions made about it while walking a
2973 * Discovery Log Page, so the whole outcome for one entry (or, with @e
2974 * NULL, for the primary's own self entry) can be logged in one place.
2975 */
2976struct dc_decision {
2977 struct libnvme_ctrl *c;
2978 bool_Bool primary;
2979 bool_Bool already_connected;
2980 bool_Bool connect;
2981 bool_Bool disconnect;
2982};
2983
2984static void dc_log_decision(struct libnvmf_context *fctx,
2985 struct nvmf_disc_log_entry *e, const struct dc_decision *d,
2986 const char *reason)
2987{
2988 const char *subnqn, *transport, *traddr, *trsvcid;
2989 const char *ctrl_name = d->c ? libnvme_ctrl_get_name(d->c) : NULL((void*)0);
2990 __u16 eflags = 0;
2991
2992 if (e) {
2993 subnqn = e->subnqn;
2994 transport = libnvmf_trtype_str(e->trtype);
2995 traddr = e->traddr;
2996 trsvcid = e->trsvcid;
2997 eflags = le16_to_cpu(e->eflags);
2998 } else if (d->c) {
2999 subnqn = libnvme_ctrl_get_subsysnqn(d->c);
3000 transport = libnvme_ctrl_get_transport(d->c);
3001 traddr = libnvme_ctrl_get_traddr(d->c);
3002 trsvcid = libnvme_ctrl_get_trsvcid(d->c);
3003 } else {
3004 subnqn = transport = traddr = trsvcid = "-";
3005 }
3006
3007 libnvme_msg(fctx->ctx, LIBNVME_LOG_DEBUG,__libnvme_msg(fctx->ctx, LIBNVME_LOG_DEBUG, ((void*)0), "discover: %s transport %s traddr %s trsvcid %s eflags 0x%04x primary=%d already_connected=%d connect=%d disconnect=%d ctrl=%s%s%s\n"
, subnqn, transport, traddr, trsvcid, eflags, d->primary, d
->already_connected, d->connect, d->disconnect, ctrl_name
? ctrl_name : "-", reason ? " reason=" : "", reason ? reason
: "")
3008 "discover: %s transport %s traddr %s trsvcid %s eflags 0x%04x primary=%d already_connected=%d connect=%d disconnect=%d ctrl=%s%s%s\n",__libnvme_msg(fctx->ctx, LIBNVME_LOG_DEBUG, ((void*)0), "discover: %s transport %s traddr %s trsvcid %s eflags 0x%04x primary=%d already_connected=%d connect=%d disconnect=%d ctrl=%s%s%s\n"
, subnqn, transport, traddr, trsvcid, eflags, d->primary, d
->already_connected, d->connect, d->disconnect, ctrl_name
? ctrl_name : "-", reason ? " reason=" : "", reason ? reason
: "")
3009 subnqn, transport, traddr, trsvcid, eflags, d->primary,__libnvme_msg(fctx->ctx, LIBNVME_LOG_DEBUG, ((void*)0), "discover: %s transport %s traddr %s trsvcid %s eflags 0x%04x primary=%d already_connected=%d connect=%d disconnect=%d ctrl=%s%s%s\n"
, subnqn, transport, traddr, trsvcid, eflags, d->primary, d
->already_connected, d->connect, d->disconnect, ctrl_name
? ctrl_name : "-", reason ? " reason=" : "", reason ? reason
: "")
3010 d->already_connected, d->connect, d->disconnect,__libnvme_msg(fctx->ctx, LIBNVME_LOG_DEBUG, ((void*)0), "discover: %s transport %s traddr %s trsvcid %s eflags 0x%04x primary=%d already_connected=%d connect=%d disconnect=%d ctrl=%s%s%s\n"
, subnqn, transport, traddr, trsvcid, eflags, d->primary, d
->already_connected, d->connect, d->disconnect, ctrl_name
? ctrl_name : "-", reason ? " reason=" : "", reason ? reason
: "")
3011 ctrl_name ? ctrl_name : "-", reason ? " reason=" : "",__libnvme_msg(fctx->ctx, LIBNVME_LOG_DEBUG, ((void*)0), "discover: %s transport %s traddr %s trsvcid %s eflags 0x%04x primary=%d already_connected=%d connect=%d disconnect=%d ctrl=%s%s%s\n"
, subnqn, transport, traddr, trsvcid, eflags, d->primary, d
->already_connected, d->connect, d->disconnect, ctrl_name
? ctrl_name : "-", reason ? " reason=" : "", reason ? reason
: "")
3012 reason ? reason : "")__libnvme_msg(fctx->ctx, LIBNVME_LOG_DEBUG, ((void*)0), "discover: %s transport %s traddr %s trsvcid %s eflags 0x%04x primary=%d already_connected=%d connect=%d disconnect=%d ctrl=%s%s%s\n"
, subnqn, transport, traddr, trsvcid, eflags, d->primary, d
->already_connected, d->connect, d->disconnect, ctrl_name
? ctrl_name : "-", reason ? " reason=" : "", reason ? reason
: "")
;
3013}
3014
3015static void dc_already_connected(struct libnvmf_context *fctx,
3016 struct libnvme_host *h, struct nvmf_disc_log_entry *e)
3017{
3018 if (fctx->hooks.already_connected)
3019 fctx->hooks.already_connected(fctx, h, e->subnqn,
3020 libnvmf_trtype_str(e->trtype), e->traddr,
3021 e->trsvcid, fctx->hooks.user_data);
3022}
3023
3024/*
3025 * The spec does not require processing referral entries deeper than
3026 * eight levels.
3027 */
3028#define NVMF_MAX_REFERRAL_DEPTH8 8
3029
3030/*
3031 * Every discovery controller visited during one top-level walk, keyed by
3032 * TID. The depth cap alone is not a cycle guard -- a referral graph can
3033 * cycle back to an earlier DC within eight hops -- so this is what
3034 * actually stops the walk from looping.
3035 */
3036SHR_PTRARRAY_DEFINE(dc_visited, struct libnvmf_tid)struct dc_visited { struct libnvmf_tid **items; size_t len; size_t
cap; }; static inline int dc_visited_append(struct dc_visited
*a, struct libnvmf_tid *item) { do { (void) sizeof(char [1 -
2*!(sizeof(struct dc_visited) == sizeof(struct shr_ptrarray)
)]); } while(0); return shr_ptrarray_append( (struct shr_ptrarray
*)a, item); } static inline void dc_visited_free(struct dc_visited
*a) { shr_ptrarray_free((struct shr_ptrarray *)a); }
;
3037
3038static bool_Bool dc_visited_has(const struct dc_visited *v,
3039 const struct libnvmf_tid *tid)
3040{
3041 const char *canon = libnvmf_tid_get_canonical(tid);
3042
3043 if (!canon)
3044 return false0;
3045
3046 for (size_t i = 0; i < v->len; i++) {
3047 const char *seen = libnvmf_tid_get_canonical(v->items[i]);
3048
3049 if (seen && !strcmp(seen, canon))
3050 return true1;
3051 }
3052
3053 return false0;
3054}
3055
3056/*
3057 * Registers c's own real, connected identity -- not whatever a referring
3058 * entry claimed about it -- so a cycle that loops back to c (including
3059 * back to the primary) is recognized even if reached via a differently
3060 * reported path. Best-effort: a TID construction failure here just means
3061 * this one DC isn't deduplicated, not a fatal error for the walk.
3062 */
3063static void dc_visited_register(struct dc_visited *visited,
3064 struct libnvme_ctrl *c, struct libnvmf_context *fctx)
3065{
3066 struct libnvmf_tid *tid;
3067 int err;
3068
3069 err = libnvmf_tid_from_fields(libnvme_ctrl_get_transport(c),
3070 libnvme_ctrl_get_traddr(c), libnvme_ctrl_get_trsvcid(c),
3071 libnvme_ctrl_get_subsysnqn(c),
3072 libnvme_ctrl_get_host_traddr(c),
3073 libnvme_ctrl_get_host_iface(c),
3074 fctx->hostnqn, fctx->hostid, &tid);
3075 if (err)
3076 return;
3077
3078 if (dc_visited_append(visited, tid))
3079 libnvmf_tid_free(tid);
3080}
3081
3082static inline void free_libnvmf_tid(struct libnvmf_tid **tid)
3083{
3084 libnvmf_tid_free(*tid);
3085}
3086#define __cleanup_libnvmf_tid__attribute__((cleanup(free_libnvmf_tid))) __cleanup(free_libnvmf_tid)__attribute__((cleanup(free_libnvmf_tid)))
3087
3088/*
3089 * A leaf entry (NVME_NQN_NVME, an I/O controller) is a dead end for the
3090 * walk: connect it if --connect was requested, and there is nothing
3091 * further to recurse into.
3092 */
3093static void dc_connect_leaf_entry(struct libnvme_global_ctx *ctx,
3094 struct libnvmf_context *fctx, struct libnvme_host *h,
3095 struct nvmf_disc_log_entry *e,
3096 struct libnvme_ctrl_params *params)
3097{
3098 struct dc_decision d = { 0 };
3099 int err;
3100
3101 if (!fctx->connect) {
3102 dc_log_decision(fctx, e, &d, "connect not requested");
3103 return;
3104 }
3105
3106 if (fctx->hooks.connect_leaf) {
3107 /*
3108 * NBFT's own leaf-connect quirks (DHCP retry, firing
3109 * hooks.connected) apply instead -- it fires the hook
3110 * itself.
3111 */
3112 err = fctx->hooks.connect_leaf(ctx, fctx, h, e, params, &d.c);
3113 } else {
3114 err = nvmf_connect_disc_entry(h, e, params, &d.c);
3115 if (d.c && fctx->hooks.connected)
3116 fctx->hooks.connected(fctx, d.c, fctx->hooks.user_data);
3117 }
3118 if (d.c) {
3119 d.connect = true1;
3120 dc_log_decision(fctx, e, &d, NULL((void*)0));
3121 } else if (err == -ENVME_CONNECT_ALREADY) {
3122 dc_already_connected(fctx, h, e);
3123 } else {
3124 dc_log_decision(fctx, e, &d, libnvme_strerror(-err));
3125 }
3126}
3127
3128static int _nvmf_discover(struct libnvme_global_ctx *ctx,
3129 struct libnvmf_context *fctx,
3130 const struct libnvme_ctrl_params *ctrl_params,
3131 struct libnvme_ctrl *c, enum dc_ownership own, int depth,
3132 struct dc_visited *visited, const __u16 *parent_eflags,
3133 bool_Bool *disconnected);
3134
3135/*
3136 * A referral entry (NVME_NQN_DISC) is a branch point: it opens up another
3137 * Discovery Service to walk, unlike a leaf entry.
3138 */
3139static void dc_walk_referral(struct libnvme_global_ctx *ctx,
3140 struct libnvmf_context *fctx, struct libnvme_host *h,
3141 struct nvmf_disc_log_entry *e,
3142 struct libnvme_ctrl_params *params, int depth,
3143 struct dc_visited *visited, __u16 eflags)
3144{
3145 __cleanup_libnvmf_tid__attribute__((cleanup(free_libnvmf_tid))) struct libnvmf_tid *tid = NULL((void*)0);
3146 struct dc_decision d = { 0 };
3147 enum dc_ownership child_own;
3148 bool_Bool child_disconnected = false0;
3149 struct libnvme_ctrl *cl;
3150 int err;
3151
3152 if (depth >= NVMF_MAX_REFERRAL_DEPTH8) {
3153 dc_log_decision(fctx, e, &d,
3154 "referral depth limit reached, not descending");
3155 return;
3156 }
3157
3158 if (libnvmf_tid_from_fields(params->transport, params->traddr,
3159 params->trsvcid, params->subsysnqn,
3160 params->host_traddr, params->host_iface,
3161 fctx->hostnqn, fctx->hostid, &tid)) {
3162 dc_log_decision(fctx, e, &d, "invalid entry");
3163 return;
3164 }
3165
3166 if (dc_visited_has(visited, tid)) {
3167 dc_log_decision(fctx, e, &d, "already visited this walk");
3168 return;
3169 }
3170
3171 cl = lookup_live_ctrl(h, params);
3172 if (cl) {
3173 d.c = cl;
3174 d.already_connected = true1;
3175 child_own = DC_BORROWED;
3176 } else {
3177 set_discovery_kato(fctx, params);
3178 err = nvmf_connect_disc_entry(h, e, params, &d.c);
3179 if (!d.c) {
3180 if (err == -ENVME_CONNECT_ALREADY)
3181 dc_already_connected(fctx, h, e);
3182 else
3183 dc_log_decision(fctx, e, &d,
3184 libnvme_strerror(-err));
3185 return;
3186 }
3187 child_own = DC_OWNED;
3188 if (fctx->hooks.connected)
3189 fctx->hooks.connected(fctx, d.c, fctx->hooks.user_data);
3190 }
3191 d.connect = true1;
3192 dc_log_decision(fctx, e, &d, NULL((void*)0));
3193
3194 if (tid) {
3195 if (!dc_visited_append(visited, tid))
3196 tid = NULL((void*)0); /* ownership transferred */
3197 }
3198
3199 /*
3200 * The child decides its own fate (disconnect or persist)
3201 * internally, the same way this DC just did above for
3202 * itself. Only free it if it actually disconnected --
3203 * a persisted child must stay in the tree, since a sibling
3204 * or later referral elsewhere in this same walk may still
3205 * need to find it via lookup_live_ctrl()/dc_visited_has().
3206 */
3207 _nvmf_discover(ctx, fctx, params, d.c, child_own, depth + 1,
3208 visited, &eflags, &child_disconnected);
3209 if (child_disconnected)
3210 libnvme_free_ctrl(d.c);
3211}
3212
3213/*
3214 * Is @e the self entry for the DC's own connection (@c)? A multi-homed DC may
3215 * report one self entry per port (Base spec 2.4, Figure 320, subtype 03h);
3216 * only the entry matching @c's transport, traddr and trsvcid is this same
3217 * connection, so only its EFLAGS apply here. The others describe the DC's
3218 * other ports.
3219 */
3220static bool_Bool dc_entry_is_self(const struct libnvme_ctrl *c,
3221 const struct nvmf_disc_log_entry *e)
3222{
3223 return e->subtype == NVME_NQN_CURR &&
3224 shr_streq0(c->transport, libnvmf_trtype_str(e->trtype)) &&
3225 shr_streq0(c->traddr, e->traddr) &&
3226 shr_streq0(c->trsvcid, e->trsvcid);
3227}
3228
3229/*
3230 * Pass 1: a first pass over the DLP entries to sanitize them and survey
3231 * this DC's own self entry (SUBTYPE 03h) -- the only place this DC's own
3232 * EPCSD is ever reported. Returns whether c should be disconnected once
3233 * its own Discovery Log Page has been fully walked.
3234 *
3235 * Sanitizing here, not right after the fetch, is deliberate: fctx->hooks
3236 * .discovery_log fires before this runs, and it must see the log page
3237 * exactly as the DC returned it (e.g. for --raw). Only Pass 2's connect
3238 * logic, which runs after this pass completes, needs the guaranteed-
3239 * terminated strings this pass produces.
3240 */
3241static bool_Bool dc_survey_self_entry(struct libnvmf_context *fctx,
3242 struct libnvme_ctrl *c, enum dc_ownership own, bool_Bool primary,
3243 const __u16 *parent_eflags, struct nvmf_discovery_log *log,
3244 uint64_t numrec)
3245{
3246 struct nvmf_disc_log_entry *self_entry = NULL((void*)0);
3247 struct dc_decision d = {
3248 .c = c,
3249 .primary = primary,
3250 .already_connected = own == DC_BORROWED,
3251 };
3252 bool_Bool self_seen;
3253 __u16 self_eflags;
3254
3255 for (uint64_t i = 0; i < numrec; i++) {
3256 struct nvmf_disc_log_entry *e = &log->entries[i];
3257
3258 sanitize_discovery_log_entry(c->ctx, e);
3259 if (dc_entry_is_self(c, e))
3260 self_entry = e;
3261 }
3262
3263 self_seen = self_entry != NULL((void*)0);
3264 self_eflags = self_seen ? le16_to_cpu(self_entry->eflags) : 0;
3265
3266 d.disconnect = dc_decide(fctx, libnvme_ctrl_get_subsysnqn(c), own,
3267 self_seen, self_eflags, parent_eflags);
3268
3269 if (self_entry) {
3270 dc_log_decision(fctx, self_entry, &d, NULL((void*)0));
3271 } else if (own == DC_BORROWED) {
3272 dc_log_decision(fctx, NULL((void*)0), &d,
3273 "pre-existing, not ours to disconnect");
3274 } else if (parent_eflags) {
3275 dc_log_decision(fctx, NULL((void*)0), &d,
3276 "no self entry, using parent's view");
3277 } else {
3278 dc_log_decision(fctx, NULL((void*)0), &d,
3279 "no self entry, EPCSD assumed 0");
3280 }
3281
3282 return d.disconnect;
3283}
3284
3285static int _nvmf_discover(struct libnvme_global_ctx *ctx,
3286 struct libnvmf_context *fctx,
3287 const struct libnvme_ctrl_params *ctrl_params,
3288 struct libnvme_ctrl *c, enum dc_ownership own, int depth,
3289 struct dc_visited *visited, const __u16 *parent_eflags,
3290 bool_Bool *disconnected)
3291{
3292 __cleanup_free__attribute__((cleanup(shr_freep))) struct nvmf_discovery_log *log = NULL((void*)0);
3293 struct libnvme_subsystem *s = libnvme_ctrl_get_subsystem(c);
3294 struct libnvme_host *h = libnvme_subsystem_get_host(s);
3295 uint64_t numrec;
3296 bool_Bool disconnect;
3297 int err;
3298
3299 if (disconnected)
3300 *disconnected = false0;
3301
3302 struct libnvmf_discovery_args args = {
3303 .max_retries = fctx->default_max_discovery_retries,
3304 .lsp = NVMF_LOG_DISC_LSP_NONE,
3305 };
3306
3307 err = nvme_discovery_log(c, &args, &log);
3308 if (err) {
3309 libnvme_msg(ctx, LIBNVME_LOG_ERR,__libnvme_msg(ctx, LIBNVME_LOG_ERR, ((void*)0), "failed to get discovery log: %s\n"
, libnvme_strerror(-err))
3310 "failed to get discovery log: %s\n",__libnvme_msg(ctx, LIBNVME_LOG_ERR, ((void*)0), "failed to get discovery log: %s\n"
, libnvme_strerror(-err))
3311 libnvme_strerror(-err))__libnvme_msg(ctx, LIBNVME_LOG_ERR, ((void*)0), "failed to get discovery log: %s\n"
, libnvme_strerror(-err))
;
3312 return err;
3313 }
3314
3315 numrec = le64_to_cpu(log->numrec);
3316
3317 if (fctx->hooks.discovery_log)
3318 fctx->hooks.discovery_log(fctx, log, numrec,
3319 fctx->hooks.user_data);
3320
3321 dc_visited_register(visited, c, fctx);
3322
3323 /*
3324 * Pass 1: survey c's own self entry, and determine if safe to
3325 * disconnect.
3326 */
3327 disconnect = dc_survey_self_entry(fctx, c, own, !parent_eflags,
3328 parent_eflags, log, numrec);
3329
3330 /*
3331 * Safe to disconnect now, before walking c's own referrals: this
3332 * connection is only ever needed to fetch c's own DLP, and
3333 * dc_visited already recorded c, independent of whether the
3334 * connection stays open.
3335 */
3336 if (disconnect) {
3337 libnvmf_disconnect_ctrl(c);
3338 if (disconnected)
3339 *disconnected = true1;
3340 }
3341
3342 /*
3343 * Pass 2: everything else -- referrals to walk, NVM subsystem
3344 * entries to connect per --connect. Self entries are already
3345 * handled above.
3346 */
3347 for (uint64_t i = 0; i < numrec; i++) {
3348 struct nvmf_disc_log_entry *e = &log->entries[i];
3349 __cleanup_ctrl_params__attribute__((cleanup(cleanup_ctrl_params))) struct libnvme_ctrl_params params =
3350 ctrl_params_dup(ctrl_params);
3351 struct dc_decision d = { 0 };
3352 const char *transport;
3353 __u16 eflags;
3354
3355 /*
3356 * A self entry describes this same Discovery subsystem.
3357 * Real DCs publish one for the port already connected;
3358 * a multi-port DC may also list its other ports. Base spec
3359 * 2.4 makes acting on either a "may", and a log page entry
3360 * carries no source address or interface, so the only local
3361 * path available is the one this connection already uses.
3362 */
3363 if (e->subtype == NVME_NQN_CURR)
3364 continue;
3365
3366 /* Skip connect if the transport types don't match */
3367 transport = libnvmf_trtype_str(e->trtype);
3368 if (strcmp(libnvme_ctrl_get_transport(c), transport)) {
3369 dc_log_decision(fctx, e, &d, "transport mismatch");
3370 continue;
3371 }
3372
3373 params.subsysnqn = e->subnqn;
3374 params.transport = transport;
3375 params.traddr = e->traddr;
3376 params.trsvcid = e->trsvcid;
3377 eflags = le16_to_cpu(e->eflags);
3378
3379 if (e->subtype == NVME_NQN_NVME) {
3380 dc_connect_leaf_entry(ctx, fctx, h, e, &params);
3381 continue;
3382 }
3383
3384 /* NVME_NQN_DISC: a referral to another Discovery Service. */
3385 dc_walk_referral(ctx, fctx, h, e, &params, depth, visited,
3386 eflags);
3387 }
3388
3389 return 0;
3390}
3391
3392/*
3393 * Owns the whole-walk visited-set lifetime, so callers don't need to know
3394 * it exists. c has no parent -- it is the primary discovery controller
3395 * connection -- so the walk starts at depth 0 with no parent eflags.
3396 */
3397static int dc_walk(struct libnvme_global_ctx *ctx, struct libnvmf_context *fctx,
3398 struct libnvme_ctrl *c, enum dc_ownership own)
3399{
3400 struct dc_visited visited = { 0 };
3401 int err;
3402
3403 err = _nvmf_discover(ctx, fctx, &fctx->ctrl_params, c, own, 0,
3404 &visited, NULL((void*)0), NULL((void*)0));
3405
3406 /* dc_visited owns only the backing array, not the TIDs in it. */
3407 for (size_t i = 0; i < visited.len; i++)
3408 libnvmf_tid_free(visited.items[i]);
3409 dc_visited_free(&visited);
3410
3411 return err;
3412}
3413
3414__shr_public__attribute__((visibility("default"))) const char *libnvmf_get_default_trsvcid(const char *transport,
3415 bool_Bool discovery_ctrl)
3416{
3417 if (!transport)
3418 return NULL((void*)0);
3419 if (!strcmp(transport, "tcp")) {
3420 if (discovery_ctrl)
3421 /* Default port for NVMe/TCP discovery controllers */
3422 return stringify(NVME_DISC_IP_PORT)"8009";
3423 /* Default port for NVMe/TCP io controllers */
3424 return stringify(NVME_RDMA_IP_PORT)"4420";
3425 } else if (!strcmp(transport, "rdma")) {
3426 /* Default port for NVMe/RDMA controllers */
3427 return stringify(NVME_RDMA_IP_PORT)"4420";
3428 }
3429
3430 return NULL((void*)0);
3431}
3432
3433static int libnvme_add_ctrl(struct libnvmf_context *fctx,
3434 struct libnvme_host *h, struct libnvme_ctrl *c)
3435{
3436 int err;
3437
3438retry:
3439 err = libnvmf_add_ctrl(h, c);
3440 if (!err)
3441 return 0;
3442 if (fctx->hooks.decide_retry &&
3443 fctx->hooks.decide_retry(fctx, err, fctx->hooks.user_data))
3444 goto retry;
3445
3446 return err;
3447}
3448
3449static int __create_discovery_ctrl(struct libnvme_global_ctx *ctx,
3450 struct libnvmf_context *fctx, struct libnvme_ctrl_params *params,
3451 struct libnvme_host *h, struct libnvme_ctrl **ctrl)
3452{
3453 struct libnvme_ctrl *c;
3454 int tmo, ret;
3455
3456 ret = libnvme_create_ctrl(ctx, params, &c);
3457 if (ret)
3458 return ret;
3459
3460 libnvme_ctrl_set_discovery_ctrl(c, true1);
3461 libnvme_ctrl_set_unique_discovery_ctrl(c,
3462 strcmp(params->subsysnqn, NVME_DISC_SUBSYS_NAME"nqn.2014-08.org.nvmexpress.discovery"));
3463 tmo = set_discovery_kato(fctx, params);
3464
3465 if (libnvme_ctrl_get_unique_discovery_ctrl(c) && fctx->hostkey) {
3466 libnvme_ctrl_set_kxchap_host_key(c, fctx->hostkey);
3467 if (fctx->ctrlkey)
3468 libnvme_ctrl_set_kxchap_ctrl_key(c, fctx->ctrlkey);
3469 }
3470
3471 ret = libnvme_add_ctrl(fctx, h, c);
3472 params->cfg.keep_alive_tmo = tmo;
3473 if (ret) {
3474 libnvme_free_ctrl(c);
3475 return ret;
3476 }
3477
3478 *ctrl = c;
3479 return 0;
3480}
3481
3482static int nvmf_create_discovery_ctrl(struct libnvme_global_ctx *ctx,
3483 struct libnvmf_context *fctx, struct libnvme_ctrl_params *params,
3484 struct libnvme_host *h, struct libnvme_ctrl **ctrl)
3485{
3486 __cleanup_libnvme_free__attribute__((cleanup(libnvme_freep))) struct nvme_id_ctrl *id = NULL((void*)0);
3487 struct libnvme_ctrl *c;
3488 int ret;
3489
3490 ret = __create_discovery_ctrl(ctx, fctx, params, h, &c);
3491 if (ret)
3492 return ret;
3493
3494 if (libnvme_ctrl_get_unique_discovery_ctrl(c)) {
3495 *ctrl = c;
3496 return 0;
3497 }
3498
3499 id = libnvme_alloc(sizeof(*id));
3500 if (!id) {
3501 libnvme_free_ctrl(c);
3502 return -ENOMEM12;
3503 }
3504
3505 ret = libnvme_open(ctx, c->name, O_RDONLY00, &c->hdl);
3506 if (ret) {
3507 libnvme_msg(ctx, LIBNVME_LOG_ERR, "failed to open %s\n", c->name)__libnvme_msg(ctx, LIBNVME_LOG_ERR, ((void*)0), "failed to open %s\n"
, c->name)
;
3508 return ret;
3509 }
3510
3511 /* Find out the name of discovery controller */
3512 ret = libnvme_ctrl_identify(c, id);
3513 if (ret) {
3514 libnvme_msg(ctx, LIBNVME_LOG_ERR,__libnvme_msg(ctx, LIBNVME_LOG_ERR, ((void*)0), "failed to identify controller, error %s\n"
, libnvme_strerror(-ret))
3515 "failed to identify controller, error %s\n",__libnvme_msg(ctx, LIBNVME_LOG_ERR, ((void*)0), "failed to identify controller, error %s\n"
, libnvme_strerror(-ret))
3516 libnvme_strerror(-ret))__libnvme_msg(ctx, LIBNVME_LOG_ERR, ((void*)0), "failed to identify controller, error %s\n"
, libnvme_strerror(-ret))
;
3517 libnvmf_disconnect_ctrl(c);
3518 libnvme_free_ctrl(c);
3519 return ret;
3520 }
3521
3522 if (!strcmp(id->subnqn, NVME_DISC_SUBSYS_NAME"nqn.2014-08.org.nvmexpress.discovery")) {
3523 *ctrl = c;
3524 return 0;
3525 }
3526
3527 /*
3528 * The subsysnqn is not the well-known name. Prefer the unique
3529 * subsysnqn over the well-known one.
3530 */
3531 libnvmf_disconnect_ctrl(c);
3532 libnvme_free_ctrl(c);
3533
3534 params->subsysnqn = id->subnqn;
3535 ret = __create_discovery_ctrl(ctx, fctx, params, h, &c);
3536 if (ret)
3537 return ret;
3538
3539 *ctrl = c;
3540 return 0;
3541}
3542
3543static struct libnvme_ctrl *dc_open_by_device(struct libnvme_global_ctx *ctx,
3544 struct libnvmf_context *fctx,
3545 struct libnvme_ctrl_params *params, enum dc_ownership *own)
3546{
3547 struct libnvme_ctrl *c;
3548 int err;
3549
3550 err = libnvme_scan_ctrl(ctx, fctx->device, &c);
3551 if (err) {
3552 /* No controller found, fall back to creating one. */
3553 libnvme_msg(ctx, LIBNVME_LOG_ERR,__libnvme_msg(ctx, LIBNVME_LOG_ERR, ((void*)0), "ctrl device %s not found\n"
, fctx->device)
3554 "ctrl device %s not found\n", fctx->device)__libnvme_msg(ctx, LIBNVME_LOG_ERR, ((void*)0), "ctrl device %s not found\n"
, fctx->device)
;
3555 return NULL((void*)0);
3556 }
3557
3558 /* Check if device matches command-line options */
3559 if (!libnvmf_ctrl_match_config(c, params)) {
3560 libnvme_msg(ctx, LIBNVME_LOG_ERR,__libnvme_msg(ctx, LIBNVME_LOG_ERR, ((void*)0), "ctrl device %s found, ignoring non matching command-line options\n"
, fctx->device)
3561 "ctrl device %s found, ignoring non matching command-line options\n",__libnvme_msg(ctx, LIBNVME_LOG_ERR, ((void*)0), "ctrl device %s found, ignoring non matching command-line options\n"
, fctx->device)
3562 fctx->device)__libnvme_msg(ctx, LIBNVME_LOG_ERR, ((void*)0), "ctrl device %s found, ignoring non matching command-line options\n"
, fctx->device)
;
3563 }
3564
3565 if (!libnvme_ctrl_get_discovery_ctrl(c)) {
3566 libnvme_msg(ctx, LIBNVME_LOG_ERR,__libnvme_msg(ctx, LIBNVME_LOG_ERR, ((void*)0), "ctrl device %s found, ignoring non discovery controller\n"
, fctx->device)
3567 "ctrl device %s found, ignoring non discovery controller\n",__libnvme_msg(ctx, LIBNVME_LOG_ERR, ((void*)0), "ctrl device %s found, ignoring non discovery controller\n"
, fctx->device)
3568 fctx->device)__libnvme_msg(ctx, LIBNVME_LOG_ERR, ((void*)0), "ctrl device %s found, ignoring non discovery controller\n"
, fctx->device)
;
3569
3570 libnvme_free_ctrl(c);
3571 return NULL((void*)0);
3572 }
3573
3574 /*
3575 * The controller device was found, so this is an already-existing
3576 * connection: it must not be disconnected on exit. Record that fact
3577 * locally instead of overriding fctx->persistent, which must keep
3578 * reflecting what the user actually asked for.
3579 */
3580 *own = DC_BORROWED;
3581
3582 /*
3583 * When --host-traddr/--host-iface are not specified on the
3584 * command line, use the discovery controller's (c) host-
3585 * traddr/host-iface for the connections to controllers
3586 * returned in the Discovery Log Pages. This is essential
3587 * when invoking "connect-all" with --device to reuse an
3588 * existing persistent discovery controller (as is done
3589 * for the udev rules). This ensures that host-traddr/
3590 * host-iface are consistent with the discovery controller (c).
3591 */
3592 if (!params->host_traddr)
3593 params->host_traddr = (char *)libnvme_ctrl_get_host_traddr(c);
3594 if (!params->host_iface)
3595 params->host_iface = (char *)libnvme_ctrl_get_host_iface(c);
3596
3597 return c;
3598}
3599
3600/*
3601 * Ownership is reported separately (@own) rather than folded into
3602 * fctx->persistent, which must keep reflecting what the user actually
3603 * asked for regardless of whether this connection is reused or fresh.
3604 *
3605 * @honor_no_reuse: false makes this ignore --no-reuse and always attempt
3606 * reuse first. NBFT's boot-time auto-connect has no
3607 * concept of --no-reuse and always tries to reuse an
3608 * existing connection first.
3609 */
3610static int dc_open(struct libnvme_global_ctx *ctx, struct libnvmf_context *fctx,
3611 struct libnvme_host *h, struct libnvme_ctrl_params *params,
3612 bool_Bool honor_no_reuse, enum dc_ownership *own,
3613 struct libnvme_ctrl **ctrl)
3614{
3615 struct libnvme_ctrl *c = NULL((void*)0);
3616 enum dc_ownership local_own = DC_OWNED;
3617 int err;
3618
3619 if (!honor_no_reuse || !fctx->no_reuse) {
3620 if (fctx->device)
3621 c = dc_open_by_device(ctx, fctx, params, &local_own);
3622
3623 if (!c) {
3624 c = lookup_ctrl(h, params);
3625 if (c)
3626 local_own = DC_BORROWED;
3627 }
3628 }
3629
3630 if (c) {
3631 if (!libnvme_ctrl_get_transport_handle(c)) {
3632 /*
3633 * When we found an existing controller it might not
3634 * have a device handle yet
3635 */
3636 err = libnvme_open(ctx, c->name, O_RDONLY00, &c->hdl);
3637 if (err) {
3638 libnvme_msg(ctx, LIBNVME_LOG_ERR,__libnvme_msg(ctx, LIBNVME_LOG_ERR, ((void*)0), "failed to open %s\n"
, c->name)
3639 "failed to open %s\n", c->name)__libnvme_msg(ctx, LIBNVME_LOG_ERR, ((void*)0), "failed to open %s\n"
, c->name)
;
3640 return err;
3641 }
3642 }
3643 } else {
3644 /*
3645 * No existing controller, or --no-reuse was given: create a
3646 * new one.
3647 */
3648 err = nvmf_create_discovery_ctrl(ctx, fctx, params, h, &c);
3649
3650 /*
3651 * NBFT only: the OS's own DHCP client can obtain a different
3652 * local address for this HFI than the firmware had when it
3653 * built the NBFT table. Retry once without host_traddr.
3654 * fctx->nbft_hfi is NULL for every non-NBFT caller, so this
3655 * is a complete no-op for the general path.
3656 */
3657 if (err == -ENVME_CONNECT_ADDRNOTAVAIL && fctx->nbft_hfi &&
3658 !strcmp(params->transport, "tcp") &&
3659 strlen(fctx->nbft_hfi->tcp_info.dhcp_server_ipaddr) > 0) {
3660 params->host_traddr = NULL((void*)0);
3661 err = nvmf_create_discovery_ctrl(ctx, fctx, params,
3662 h, &c);
3663 }
3664
3665 if (err)
3666 return err;
3667 }
3668
3669 *own = local_own;
3670 *ctrl = c;
3671 return 0;
3672}
3673
3674#define NBFT_SYSFS_FILENAME"NBFT*" "NBFT*"
3675
3676static int nbft_filter(const struct dirent *dent)
3677{
3678 return !fnmatch(NBFT_SYSFS_FILENAME"NBFT*", dent->d_name, FNM_PATHNAME(1 << 0));
3679}
3680
3681__shr_public__attribute__((visibility("default"))) int libnvmf_nbft_read_files(
3682 struct libnvme_global_ctx *ctx, char *path,
3683 struct nbft_file_entry **head)
3684{
3685 struct nbft_file_entry *entry = NULL((void*)0);
3686 struct libnbft_info *nbft = NULL((void*)0);
3687 struct dirent **dent;
3688 char filename[PATH_MAX4096];
3689 int i, count, ret;
3690
3691 *head = NULL((void*)0);
3692
3693 count = scandir(path, &dent, nbft_filter, NULL((void*)0));
3694 if (count < 0)
3695 return -errno(*__errno_location ());
3696
3697 for (i = 0; i < count; i++) {
3698 snprintf(filename, sizeof(filename), "%s/%s", path,
3699 dent[i]->d_name);
3700
3701 ret = libnvmf_read_nbft(ctx, &nbft, filename);
3702 if (!ret) {
3703 struct nbft_file_entry *new;
3704
3705 new = calloc(1, sizeof(*new));
3706 if (!new)
3707 return -ENOMEM12;
3708 new->nbft = nbft;
3709 if (entry) {
3710 entry->next = new;
3711 entry = entry->next;
3712 } else {
3713 entry = new;
3714 *head = entry;
3715 }
3716 }
3717 free(dent[i]);
3718 }
3719 free(dent);
3720 return 0;
3721}
3722
3723__shr_public__attribute__((visibility("default"))) void libnvmf_nbft_free(
3724 struct libnvme_global_ctx *ctx, struct nbft_file_entry *head)
3725{
3726 if (!head)
3727 return;
3728
3729 while (head) {
3730 struct nbft_file_entry *next = head->next;
3731
3732 libnvmf_free_nbft(ctx, head->nbft);
3733 free(head);
3734
3735 head = next;
3736 }
3737}
3738
3739static bool_Bool validate_uri(struct libnvme_global_ctx *ctx,
3740 struct libnbft_discovery *dd,
3741 struct libnvmf_uri *uri)
3742{
3743 if (!uri) {
3744 libnvme_msg(ctx, LIBNVME_LOG_ERR,__libnvme_msg(ctx, LIBNVME_LOG_ERR, ((void*)0), "Discovery Descriptor %d: failed to parse URI %s\n"
, dd->index, dd->uri)
3745 "Discovery Descriptor %d: failed to parse URI %s\n",__libnvme_msg(ctx, LIBNVME_LOG_ERR, ((void*)0), "Discovery Descriptor %d: failed to parse URI %s\n"
, dd->index, dd->uri)
3746 dd->index, dd->uri)__libnvme_msg(ctx, LIBNVME_LOG_ERR, ((void*)0), "Discovery Descriptor %d: failed to parse URI %s\n"
, dd->index, dd->uri)
;
3747 return false0;
3748 }
3749 if (strcmp(uri->scheme, "nvme") != 0) {
3750 libnvme_msg(ctx, LIBNVME_LOG_ERR,__libnvme_msg(ctx, LIBNVME_LOG_ERR, ((void*)0), "Discovery Descriptor %d: unsupported scheme '%s'\n"
, dd->index, uri->scheme)
3751 "Discovery Descriptor %d: unsupported scheme '%s'\n",__libnvme_msg(ctx, LIBNVME_LOG_ERR, ((void*)0), "Discovery Descriptor %d: unsupported scheme '%s'\n"
, dd->index, uri->scheme)
3752 dd->index, uri->scheme)__libnvme_msg(ctx, LIBNVME_LOG_ERR, ((void*)0), "Discovery Descriptor %d: unsupported scheme '%s'\n"
, dd->index, uri->scheme)
;
3753 return false0;
3754 }
3755 if (!uri->protocol || strcmp(uri->protocol, "tcp") != 0) {
3756 libnvme_msg(ctx, LIBNVME_LOG_ERR,__libnvme_msg(ctx, LIBNVME_LOG_ERR, ((void*)0), "Discovery Descriptor %d: unsupported transport '%s'\n"
, dd->index, uri->protocol)
3757 "Discovery Descriptor %d: unsupported transport '%s'\n",__libnvme_msg(ctx, LIBNVME_LOG_ERR, ((void*)0), "Discovery Descriptor %d: unsupported transport '%s'\n"
, dd->index, uri->protocol)
3758 dd->index, uri->protocol)__libnvme_msg(ctx, LIBNVME_LOG_ERR, ((void*)0), "Discovery Descriptor %d: unsupported transport '%s'\n"
, dd->index, uri->protocol)
;
3759 return false0;
3760 }
3761
3762 return true1;
3763}
3764
3765static int nbft_connect(struct libnvme_global_ctx *ctx,
3766 struct libnvmf_context *fctx, struct libnvme_ctrl_params *params,
3767 struct libnvme_host *h, struct nvmf_disc_log_entry *e,
3768 struct libnbft_subsystem_ns *ss, struct libnvme_ctrl **cp)
3769{
3770 struct libnvme_ctrl *c;
3771 int saved_log_level;
3772 bool_Bool saved_log_tstamp;
3773 bool_Bool saved_log_pid;
3774 int ret;
3775
3776 if (cp)
3777 *cp = NULL((void*)0);
3778
3779 saved_log_level = libnvme_get_logging_level(ctx, &saved_log_pid,
3780 &saved_log_tstamp);
3781
3782 c = lookup_live_ctrl(h, params);
3783 if (c) {
3784 if (cp)
3785 *cp = c;
3786 return 0;
3787 }
3788
3789 if (nvmf_excluded(ctx, params->transport,
3790 params->traddr, params->trsvcid,
3791 params->subsysnqn,
3792 params->host_traddr,
3793 params->host_iface,
3794 libnvme_host_get_hostnqn(h),
3795 libnvme_host_get_hostid(h)))
3796 return 0;
3797
3798 ret = libnvme_create_ctrl(ctx, params, &c);
3799 if (ret)
3800 return ret;
3801
3802 /* Pause logging for unavailable SSNSs */
3803 if (ss && (ss->flags & NBFT_SSNS_UNAVAIL_NAMESPACE_UNAVAIL) && saved_log_level < 1)
3804 libnvme_set_logging_level(ctx, -1, false0, false0);
3805
3806 /* Update tls or concat */
3807 nvmf_update_tls_concat(e, c, h);
3808
3809 ret = libnvmf_add_ctrl(h, c);
3810
3811 /* Resume logging */
3812 if (ss && (ss->flags & NBFT_SSNS_UNAVAIL_NAMESPACE_UNAVAIL) && saved_log_level < 1)
3813 libnvme_set_logging_level(ctx,
3814 saved_log_level,
3815 saved_log_pid,
3816 saved_log_tstamp);
3817
3818 if (ret) {
3819 libnvme_free_ctrl(c);
3820 /*
3821 * In case this SSNS was marked as 'unavailable' and
3822 * our connection attempt has failed, ignore it.
3823 */
3824 if (ss && (ss->flags & NBFT_SSNS_UNAVAIL_NAMESPACE_UNAVAIL)) {
3825 libnvme_msg(ctx, LIBNVME_LOG_INFO,__libnvme_msg(ctx, LIBNVME_LOG_INFO, ((void*)0), "SSNS %d reported as unavailable, skipping\n"
, ss->index)
3826 "SSNS %d reported as unavailable, skipping\n",__libnvme_msg(ctx, LIBNVME_LOG_INFO, ((void*)0), "SSNS %d reported as unavailable, skipping\n"
, ss->index)
3827 ss->index)__libnvme_msg(ctx, LIBNVME_LOG_INFO, ((void*)0), "SSNS %d reported as unavailable, skipping\n"
, ss->index)
;
3828 return 0;
3829 }
3830 return ret;
3831 }
3832
3833 if (fctx->hooks.connected)
3834 fctx->hooks.connected(fctx, c, fctx->hooks.user_data);
3835
3836 if (cp)
3837 *cp = c;
3838 return 0;
3839}
3840
3841/*
3842 * connect_leaf hook for the NBFT-driven walk: nbft_connect() plus the
3843 * checks _nvmf_discover()'s general leaf-connect path gets for free from
3844 * distinguishable lookup_ctrl()/nvmf_excluded() outcomes. nbft_connect()
3845 * has its own copies of both checks internally, but folds every "nothing
3846 * to do" outcome into a plain 0 -- fine for its other caller (the SSNS
3847 * loop below), but not enough to fit dc_log_decision()'s
3848 * connected/already-connected/reason contract. Re-checking here, before
3849 * calling nbft_connect(), keeps nbft_connect() itself untouched for that
3850 * other caller.
3851 */
3852static int nbft_connect_leaf(struct libnvme_global_ctx *ctx,
3853 struct libnvmf_context *fctx, struct libnvme_host *h,
3854 struct nvmf_disc_log_entry *e,
3855 struct libnvme_ctrl_params *params, struct libnvme_ctrl **cp)
3856{
3857 struct libnvme_ctrl *c;
3858 int ret;
3859
3860 *cp = NULL((void*)0);
3861
3862 c = lookup_live_ctrl(h, params);
3863 if (c) {
3864 *cp = c;
3865 return -ENVME_CONNECT_ALREADY;
3866 }
3867
3868 if (nvmf_excluded(ctx, params->transport, params->traddr,
3869 params->trsvcid, params->subsysnqn,
3870 params->host_traddr, params->host_iface,
3871 libnvme_host_get_hostnqn(h),
3872 libnvme_host_get_hostid(h)))
3873 return -EPERM1;
3874
3875 ret = nbft_connect(ctx, fctx, params, h, e, NULL((void*)0), &c);
3876
3877 /*
3878 * With TCP/DHCP, the OS's own DHCP client can obtain a different
3879 * local address for this HFI than the firmware had. Retry once
3880 * without host_traddr.
3881 */
3882 if (ret == -ENVME_CONNECT_ADDRNOTAVAIL &&
3883 !strcmp(params->transport, "tcp") && fctx->nbft_hfi &&
3884 strlen(fctx->nbft_hfi->tcp_info.dhcp_server_ipaddr) > 0) {
3885 params->host_traddr = NULL((void*)0);
3886 ret = nbft_connect(ctx, fctx, params, h, e, NULL((void*)0), &c);
3887 }
3888
3889 if (!ret)
3890 *cp = c;
3891
3892 return ret;
3893}
3894
3895#define VLAN_PROC_PATH"/proc/net/vlan" "/proc/net/vlan"
3896
3897/*
3898 * Return 0 for no vlan_id, to be consistent with the NBFT spec.
3899 */
3900static int get_vlan_id(const char *ifname)
3901{
3902 char path[256], line[256];
3903 int vlan_id = 0;
3904 FILE *f;
3905
3906 snprintf(path, sizeof(path), "%s/%s", VLAN_PROC_PATH"/proc/net/vlan", ifname);
3907 f = fopen(path, "r");
3908 if (!f)
3909 return 0;
3910
3911 while (fgets(line, sizeof(line), f)) {
3912 if (sscanf(line, " VID: %d", &vlan_id) == 1) {
3913 fclose(f);
3914 return vlan_id;
3915 }
3916 }
3917
3918 fclose(f);
3919 return 0;
3920}
3921
3922/*
3923 * Find network interface corresponding to the NBFT HFI
3924 * by looking for mac address and vlan id.
3925 */
3926static char *nbft_find_hfi_iface(struct libnbft_hfi *hfi)
3927{
3928 struct ifaddrs *ifaddr, *ifa;
3929 char *result = NULL((void*)0);
3930
3931 if (strcmp((char *)hfi->transport, "tcp"))
3932 return NULL((void*)0);
3933
3934 if (getifaddrs(&ifaddr) != 0)
3935 return NULL((void*)0);
3936
3937 for (ifa = ifaddr; ifa != NULL((void*)0); ifa = ifa->ifa_next) {
3938 struct sockaddr_ll *sll;
3939
3940 if (!ifa->ifa_addr)
3941 continue;
3942
3943 if (ifa->ifa_addr->sa_family != AF_PACKET17)
3944 continue;
3945
3946 sll = (struct sockaddr_ll *)ifa->ifa_addr;
3947
3948 if (sll->sll_halen != ETH_ALEN6)
3949 continue;
3950
3951 if (!memcmp(sll->sll_addr, hfi->tcp_info.mac_addr, ETH_ALEN6)) {
3952 int vlan_id = get_vlan_id(ifa->ifa_name);
3953
3954 if (vlan_id == hfi->tcp_info.vlan) {
3955 result = strdup(ifa->ifa_name);
3956 break;
3957 }
3958 }
3959 }
3960
3961 freeifaddrs(ifaddr);
3962 return result;
3963}
3964
3965__shr_public__attribute__((visibility("default"))) int libnvmf_discover_nbft(struct libnvme_global_ctx *ctx,
3966 struct libnvmf_context *fctx)
3967{
3968 const char *hostnqn = NULL((void*)0), *hostid = NULL((void*)0), *host_traddr = NULL((void*)0);
3969 char uuid[NVME_UUID_LEN_STRING37];
3970 struct nbft_file_entry *head = NULL((void*)0), *entry;
3971 struct libnbft_subsystem_ns **ss;
3972 struct libnbft_hfi *hfi;
3973 struct libnbft_discovery **dd;
3974 struct libnvme_host *h;
3975 int ret, rr, i;
3976
3977 if (!fctx->nbft_path)
3978 return -EINVAL22;
3979
3980 ret = libnvme_get_host(ctx, fctx->hostnqn, fctx->hostid, &h);
3981 if (ret)
3982 return ret;
3983
3984 ret = setup_connection(fctx, h, false0);
3985 if (ret)
3986 return ret;
3987
3988 if (!fctx->connect)
3989 /* TODO: print discovery-type info from NBFT tables */
3990 return 0;
3991
3992 ret = libnvmf_nbft_read_files(ctx, fctx->nbft_path, &head);
3993 if (ret) {
3994 if (ret != -ENOENT2)
3995 libnvme_msg(ctx, LIBNVME_LOG_ERR,__libnvme_msg(ctx, LIBNVME_LOG_ERR, ((void*)0), "Failed to access ACPI tables directory\n"
)
3996 "Failed to access ACPI tables directory\n")__libnvme_msg(ctx, LIBNVME_LOG_ERR, ((void*)0), "Failed to access ACPI tables directory\n"
)
;
3997 else
3998 ret = 0; /* nothing to connect */
3999 goto out_free;
4000 }
4001
4002 for (entry = head; entry; entry = entry->next) {
4003 hostid = fctx->hostid;
4004 if (fctx->hostnqn)
4005 hostnqn = fctx->hostnqn;
4006 else {
4007 hostnqn = entry->nbft->host.nqn;
4008 if (!hostnqn)
4009 hostnqn = fctx->hostnqn;
4010 }
4011
4012 if (!hostid && entry->nbft->host.id && *entry->nbft->host.id) {
4013 ret = libnvme_uuid_to_string(entry->nbft->host.id, uuid);
4014 if (!ret)
4015 hostid = uuid;
4016 }
4017
4018 ret = libnvme_get_host(ctx, hostnqn, hostid, &h);
4019 if (ret)
4020 goto out_free;
4021
4022 /* Subsystem Namespace Descriptor List */
4023 for (ss = entry->nbft->subsystem_ns_list; ss && *ss; ss++)
4024 for (i = 0; i < (*ss)->num_hfis; i++) {
4025 __cleanup_ctrl_params__attribute__((cleanup(cleanup_ctrl_params))) struct libnvme_ctrl_params params =
4026 ctrl_params_copy(&fctx->ctrl_params);
4027
4028 hfi = (*ss)->hfis[i];
4029 if (!hfi) {
4030 libnvme_msg(ctx, LIBNVME_LOG_ERR,__libnvme_msg(ctx, LIBNVME_LOG_ERR, ((void*)0), "SSNS %d has no HFI at position %d\n"
, (*ss)->index, i)
4031 "SSNS %d has no HFI at position %d\n",__libnvme_msg(ctx, LIBNVME_LOG_ERR, ((void*)0), "SSNS %d has no HFI at position %d\n"
, (*ss)->index, i)
4032 (*ss)->index, i)__libnvme_msg(ctx, LIBNVME_LOG_ERR, ((void*)0), "SSNS %d has no HFI at position %d\n"
, (*ss)->index, i)
;
4033 ret = -EINVAL22;
4034 continue;
4035 }
4036
4037 /* Skip discovery NQN records */
4038 if (strcmp((*ss)->subsys_nqn,
4039 NVME_DISC_SUBSYS_NAME"nqn.2014-08.org.nvmexpress.discovery") == 0) {
4040 libnvme_msg(ctx, LIBNVME_LOG_INFO,__libnvme_msg(ctx, LIBNVME_LOG_INFO, ((void*)0), "SSNS %d points to well-known discovery NQN, skipping\n"
, (*ss)->index)
4041 "SSNS %d points to well-known discovery NQN, skipping\n",__libnvme_msg(ctx, LIBNVME_LOG_INFO, ((void*)0), "SSNS %d points to well-known discovery NQN, skipping\n"
, (*ss)->index)
4042 (*ss)->index)__libnvme_msg(ctx, LIBNVME_LOG_INFO, ((void*)0), "SSNS %d points to well-known discovery NQN, skipping\n"
, (*ss)->index)
;
4043 continue;
4044 }
4045
4046 if ((*ss)->security) {
4047 libnvme_msg(ctx, LIBNVME_LOG_ERR,__libnvme_msg(ctx, LIBNVME_LOG_ERR, ((void*)0), "SSNS %d has Security Profile Descriptor %d associated, security profile descriptors are currently unimplemented, skipping\n"
, (*ss)->index, (*ss)->security->index)
4048 "SSNS %d has Security Profile Descriptor %d associated, security profile descriptors are currently unimplemented, skipping\n",__libnvme_msg(ctx, LIBNVME_LOG_ERR, ((void*)0), "SSNS %d has Security Profile Descriptor %d associated, security profile descriptors are currently unimplemented, skipping\n"
, (*ss)->index, (*ss)->security->index)
4049 (*ss)->index,__libnvme_msg(ctx, LIBNVME_LOG_ERR, ((void*)0), "SSNS %d has Security Profile Descriptor %d associated, security profile descriptors are currently unimplemented, skipping\n"
, (*ss)->index, (*ss)->security->index)
4050 (*ss)->security->index)__libnvme_msg(ctx, LIBNVME_LOG_ERR, ((void*)0), "SSNS %d has Security Profile Descriptor %d associated, security profile descriptors are currently unimplemented, skipping\n"
, (*ss)->index, (*ss)->security->index)
;
4051 continue;
4052 }
4053
4054 params.host_traddr = NULL((void*)0);
4055 if (!fctx->ctrl_params.host_traddr &&
4056 !strncmp((*ss)->transport, "tcp", 3))
4057 params.host_traddr =
4058 hfi->tcp_info.ipaddr;
4059
4060 params.subsysnqn = (*ss)->subsys_nqn;
4061 params.transport = (*ss)->transport;
4062 params.traddr = (*ss)->traddr;
4063 params.trsvcid = (*ss)->trsvcid;
4064 params.host_iface = nbft_find_hfi_iface(hfi);
4065 if (!params.host_iface)
4066 libnvme_msg(ctx, LIBNVME_LOG_INFO,__libnvme_msg(ctx, LIBNVME_LOG_INFO, ((void*)0), "SSNS %d: could not find host interface for HFI %d\n"
, (*ss)->index, hfi->index)
4067 "SSNS %d: could not find host interface for HFI %d\n",__libnvme_msg(ctx, LIBNVME_LOG_INFO, ((void*)0), "SSNS %d: could not find host interface for HFI %d\n"
, (*ss)->index, hfi->index)
4068 (*ss)->index, hfi->index)__libnvme_msg(ctx, LIBNVME_LOG_INFO, ((void*)0), "SSNS %d: could not find host interface for HFI %d\n"
, (*ss)->index, hfi->index)
;
4069
4070 rr = nbft_connect(ctx, fctx, &params, h, NULL((void*)0),
4071 *ss, NULL((void*)0));
4072
4073 /*
4074 * With TCP/DHCP, it can happen that the OS
4075 * obtains a different local IP address than the
4076 * firmware had. Retry without host_traddr.
4077 */
4078 if (rr == -ENVME_CONNECT_ADDRNOTAVAIL &&
4079 !strcmp(params.transport,
4080 "tcp") &&
4081 strlen(hfi->tcp_info.dhcp_server_ipaddr) > 0) {
4082 params.host_traddr = NULL((void*)0);
4083
4084 rr = nbft_connect(ctx, fctx, &params, h,
4085 NULL((void*)0), *ss, NULL((void*)0));
4086
4087 if (rr == 0)
4088 libnvme_msg(ctx, LIBNVME_LOG_INFO,__libnvme_msg(ctx, LIBNVME_LOG_INFO, ((void*)0), "SSNS %d: connect with host_traddr=\"%s\" failed, success after omitting host_traddr\n"
, (*ss)->index, host_traddr)
4089 "SSNS %d: connect with host_traddr=\"%s\" failed, success after omitting host_traddr\n",__libnvme_msg(ctx, LIBNVME_LOG_INFO, ((void*)0), "SSNS %d: connect with host_traddr=\"%s\" failed, success after omitting host_traddr\n"
, (*ss)->index, host_traddr)
4090 (*ss)->index,__libnvme_msg(ctx, LIBNVME_LOG_INFO, ((void*)0), "SSNS %d: connect with host_traddr=\"%s\" failed, success after omitting host_traddr\n"
, (*ss)->index, host_traddr)
4091 host_traddr)__libnvme_msg(ctx, LIBNVME_LOG_INFO, ((void*)0), "SSNS %d: connect with host_traddr=\"%s\" failed, success after omitting host_traddr\n"
, (*ss)->index, host_traddr)
;
4092 }
4093
4094 if (rr) {
4095 libnvme_msg(ctx, LIBNVME_LOG_ERR,__libnvme_msg(ctx, LIBNVME_LOG_ERR, ((void*)0), "SSNS %d: no controller found\n"
, (*ss)->index)
4096 "SSNS %d: no controller found\n",__libnvme_msg(ctx, LIBNVME_LOG_ERR, ((void*)0), "SSNS %d: no controller found\n"
, (*ss)->index)
4097 (*ss)->index)__libnvme_msg(ctx, LIBNVME_LOG_ERR, ((void*)0), "SSNS %d: no controller found\n"
, (*ss)->index)
;
4098 /* report an error */
4099 ret = rr;
4100 }
4101
4102 if (rr == -ENOMEM12)
4103 goto out_free;
4104 }
4105
4106 /* Discovery Descriptor List */
4107 for (dd = entry->nbft->discovery_list; dd && *dd; dd++) {
4108 __cleanup_uri__attribute__((cleanup(free_uri))) struct libnvmf_uri *uri = NULL((void*)0);
4109 __cleanup_free__attribute__((cleanup(shr_freep))) char *trsvcid = NULL((void*)0);
4110 __cleanup_ctrl_params__attribute__((cleanup(cleanup_ctrl_params))) struct libnvme_ctrl_params params =
4111 ctrl_params_copy(&fctx->ctrl_params);
4112 enum dc_ownership own;
4113 bool_Bool linked = false0;
4114 struct libnvme_ctrl *c;
4115
4116 /* only perform discovery when no SSNS record references it */
4117 for (ss = entry->nbft->subsystem_ns_list;
4118 ss && *ss; ss++)
4119 if ((*ss)->discovery &&
4120 (*ss)->discovery->index == (*dd)->index &&
4121 /* unavailable boot attempts are not discovered
4122 * and may get transferred along with a well-known
4123 * discovery NQN into an SSNS record.
4124 */
4125 strcmp((*ss)->subsys_nqn,
4126 NVME_DISC_SUBSYS_NAME"nqn.2014-08.org.nvmexpress.discovery") != 0) {
4127 linked = true1;
4128 break;
4129 }
4130 if (linked)
4131 continue;
4132
4133 if ((*dd)->security) {
4134 libnvme_msg(ctx, LIBNVME_LOG_ERR,__libnvme_msg(ctx, LIBNVME_LOG_ERR, ((void*)0), "Discovery Descriptor %d has Security Profile Descriptor %d associated, security profile descriptors are currently unimplemented, skipping\n"
, (*dd)->index, (*dd)->security->index)
4135 "Discovery Descriptor %d has Security Profile Descriptor %d associated, security profile descriptors are currently unimplemented, skipping\n",__libnvme_msg(ctx, LIBNVME_LOG_ERR, ((void*)0), "Discovery Descriptor %d has Security Profile Descriptor %d associated, security profile descriptors are currently unimplemented, skipping\n"
, (*dd)->index, (*dd)->security->index)
4136 (*dd)->index,__libnvme_msg(ctx, LIBNVME_LOG_ERR, ((void*)0), "Discovery Descriptor %d has Security Profile Descriptor %d associated, security profile descriptors are currently unimplemented, skipping\n"
, (*dd)->index, (*dd)->security->index)
4137 (*dd)->security->index)__libnvme_msg(ctx, LIBNVME_LOG_ERR, ((void*)0), "Discovery Descriptor %d has Security Profile Descriptor %d associated, security profile descriptors are currently unimplemented, skipping\n"
, (*dd)->index, (*dd)->security->index)
;
4138 continue;
4139 }
4140
4141 hfi = (*dd)->hfi;
4142 if (!hfi) {
4143 libnvme_msg(ctx, LIBNVME_LOG_ERR,__libnvme_msg(ctx, LIBNVME_LOG_ERR, ((void*)0), "Discovery Descriptor %d has no HFI\n"
, (*dd)->index)
4144 "Discovery Descriptor %d has no HFI\n",__libnvme_msg(ctx, LIBNVME_LOG_ERR, ((void*)0), "Discovery Descriptor %d has no HFI\n"
, (*dd)->index)
4145 (*dd)->index)__libnvme_msg(ctx, LIBNVME_LOG_ERR, ((void*)0), "Discovery Descriptor %d has no HFI\n"
, (*dd)->index)
;
4146 ret = -EINVAL22;
4147 continue;
4148 }
4149 ret = libnvmf_uri_parse((*dd)->uri, &uri);
4150 if (ret)
4151 continue;
4152 if (!validate_uri(ctx, *dd, uri))
4153 continue;
4154
4155 host_traddr = NULL((void*)0);
4156 if (!fctx->ctrl_params.host_traddr &&
4157 !strncmp(uri->protocol, "tcp", 3))
4158 host_traddr = hfi->tcp_info.ipaddr;
4159 if (uri->port > 0) {
4160 if (asprintf(&trsvcid, "%d", uri->port) < 0) {
4161 ret = -ENOMEM12;
4162 goto out_free;
4163 }
4164 } else
4165 trsvcid =
4166 strdup(libnvmf_get_default_trsvcid(
4167 uri->protocol, true1));
4168
4169 params.subsysnqn = (*dd)->nqn ?
4170 (*dd)->nqn : NVME_DISC_SUBSYS_NAME"nqn.2014-08.org.nvmexpress.discovery";
4171 params.transport = uri->protocol;
4172 params.traddr = uri->host;
4173 params.trsvcid = trsvcid;
4174 params.host_traddr = host_traddr;
4175 params.host_iface = nbft_find_hfi_iface(hfi);
4176 if (!params.host_iface)
4177 libnvme_msg(ctx, LIBNVME_LOG_INFO,__libnvme_msg(ctx, LIBNVME_LOG_INFO, ((void*)0), "Discovery Descriptor %d: could not find host interface for HFI %d\n"
, (*dd)->index, hfi->index)
4178 "Discovery Descriptor %d: could not find host interface for HFI %d\n",__libnvme_msg(ctx, LIBNVME_LOG_INFO, ((void*)0), "Discovery Descriptor %d: could not find host interface for HFI %d\n"
, (*dd)->index, hfi->index)
4179 (*dd)->index, hfi->index)__libnvme_msg(ctx, LIBNVME_LOG_INFO, ((void*)0), "Discovery Descriptor %d: could not find host interface for HFI %d\n"
, (*dd)->index, hfi->index)
;
4180
4181 /*
4182 * NBFT boot discovery is a one-shot operation: never
4183 * honor --no-reuse (dc_open()'s honor_no_reuse=false
4184 * below) and never keep a freshly created connection
4185 * alive past this walk (--persistent has no meaning
4186 * here either). nbft_hfi and connect_leaf give
4187 * dc_open() and the shared walker's leaf-connect step
4188 * access to this DC's own HFI/leaf-connect quirks,
4189 * several stack frames removed from this loop.
4190 */
4191 fctx->nbft_hfi = hfi;
4192 fctx->hooks.connect_leaf = nbft_connect_leaf;
4193 fctx->persistent = LIBNVMF_PERSISTENT_NO;
4194
4195 ret = dc_open(ctx, fctx, h, &params, false0, &own, &c);
4196 if (ret) {
4197 libnvme_msg(ctx, LIBNVME_LOG_ERR,__libnvme_msg(ctx, LIBNVME_LOG_ERR, ((void*)0), "Discovery Descriptor %d: failed to add discovery controller: %s\n"
, (*dd)->index, libnvme_strerror(-ret))
4198 "Discovery Descriptor %d: failed to add discovery controller: %s\n",__libnvme_msg(ctx, LIBNVME_LOG_ERR, ((void*)0), "Discovery Descriptor %d: failed to add discovery controller: %s\n"
, (*dd)->index, libnvme_strerror(-ret))
4199 (*dd)->index, libnvme_strerror(-ret))__libnvme_msg(ctx, LIBNVME_LOG_ERR, ((void*)0), "Discovery Descriptor %d: failed to add discovery controller: %s\n"
, (*dd)->index, libnvme_strerror(-ret))
;
4200 goto out_free;
4201 }
4202
4203 rr = dc_walk(ctx, fctx, c, own);
4204 libnvme_free_ctrl(c);
4205 if (rr == -ENOMEM12) {
4206 ret = rr;
4207 goto out_free;
4208 }
4209 }
4210 }
4211out_free:
4212 libnvmf_nbft_free(ctx, head);
4213 return ret;
4214}
4215
4216__shr_public__attribute__((visibility("default"))) int libnvmf_discover(struct libnvme_global_ctx *ctx,
4217 struct libnvmf_context *fctx)
4218{
4219 struct libnvme_ctrl *c = NULL((void*)0);
4220 struct libnvme_host *h;
4221 enum dc_ownership own;
4222 int err;
4223
4224 err = libnvme_get_host(ctx, fctx->hostnqn, fctx->hostid, &h);
4225 if (err)
4226 return err;
4227
4228 err = setup_connection(fctx, h, true1);
4229 if (err)
4230 return err;
4231
4232 err = dc_open(ctx, fctx, h, &fctx->ctrl_params, true1, &own, &c);
4233 if (err) {
4234 if (err != -ENVME_CONNECT_IGNORED)
4235 libnvme_msg(ctx, LIBNVME_LOG_ERR,__libnvme_msg(ctx, LIBNVME_LOG_ERR, ((void*)0), "failed to add controller, error %s\n"
, libnvme_strerror(-err))
4236 "failed to add controller, error %s\n",__libnvme_msg(ctx, LIBNVME_LOG_ERR, ((void*)0), "failed to add controller, error %s\n"
, libnvme_strerror(-err))
4237 libnvme_strerror(-err))__libnvme_msg(ctx, LIBNVME_LOG_ERR, ((void*)0), "failed to add controller, error %s\n"
, libnvme_strerror(-err))
;
4238 return err;
4239 }
4240
4241 err = dc_walk(ctx, fctx, c, own);
4242 libnvme_free_ctrl(c);
4243
4244 return err;
4245}
4246
4247__shr_public__attribute__((visibility("default"))) int libnvmf_connect(
4248 struct libnvme_global_ctx *ctx, struct libnvmf_context *fctx)
4249{
4250 __cleanup_fd__attribute__((cleanup(shr_cleanup_fd))) int devid_fd = -1;
4251 struct libnvme_host *h;
4252 struct libnvme_ctrl *c;
4253 int err;
4254
4255 /* Open before touching kernel state, so a bad path fails fast. */
4256 if (fctx->devid_file) {
4257 devid_fd = open_devid_file(fctx);
4258 if (devid_fd < 0)
4259 return devid_fd;
4260 }
4261
4262 err = libnvme_get_host(ctx, fctx->hostnqn, fctx->hostid, &h);
4263 if (err)
4264 return err;
4265
4266 err = setup_connection(fctx, h, false0);
4267 if (err)
4268 return err;
4269
4270 c = lookup_live_ctrl(h, &fctx->ctrl_params);
4271 if (c && !fctx->ctrl_params.cfg.duplicate_connect) {
4272 int instance = ctrl_instance(c);
4273
4274 write_devid_file(fctx, devid_fd, c);
4275 if (instance >= 0)
4276 registry_update_on_connect(ctx, instance, false0);
4277 if (fctx->hooks.already_connected)
4278 fctx->hooks.already_connected(fctx, h,
4279 libnvme_ctrl_get_subsysnqn(c),
4280 libnvme_ctrl_get_transport(c),
4281 libnvme_ctrl_get_traddr(c),
4282 libnvme_ctrl_get_trsvcid(c),
4283 fctx->hooks.user_data);
4284 return -ENVME_CONNECT_ALREADY;
4285 }
4286
4287 err = libnvmf_create_ctrl(ctx, fctx, &c);
4288 if (err)
4289 return err;
4290
4291 /*
4292 * We are connecting to a discovery controller, so let's treat
4293 * this as a persistent connection and specify a KATO.
4294 */
4295 if (!strcmp(fctx->ctrl_params.subsysnqn, NVME_DISC_SUBSYS_NAME"nqn.2014-08.org.nvmexpress.discovery")) {
4296 fctx->persistent = LIBNVMF_PERSISTENT_FORCE;
4297
4298 set_discovery_kato(fctx, &fctx->ctrl_params);
4299 }
4300
4301 err = libnvme_add_ctrl(fctx, h, c);
4302 if (err) {
4303 /*
4304 * Kernel-level race: something else connected between our
4305 * scan and this ioctl. @c is our own unconnected draft, not
4306 * the winner.
4307 */
4308 if (err == -ENVME_CONNECT_ALREADY) {
4309 if (libnvme_scan_topology(ctx, NULL((void*)0), NULL((void*)0)) == 0) {
4310 struct libnvme_ctrl *winner;
4311
4312 winner = lookup_ctrl(h, &fctx->ctrl_params);
4313 if (winner) {
4314 int instance = ctrl_instance(winner);
4315
4316 write_devid_file(fctx, devid_fd, winner);
4317 if (instance >= 0)
4318 registry_update_on_connect(ctx,
4319 instance, false0);
4320 }
4321 }
4322
4323 if (fctx->hooks.already_connected)
4324 fctx->hooks.already_connected(fctx, h,
4325 fctx->ctrl_params.subsysnqn,
4326 fctx->ctrl_params.transport,
4327 fctx->ctrl_params.traddr,
4328 fctx->ctrl_params.trsvcid,
4329 fctx->hooks.user_data);
4330 } else {
4331 libnvme_msg(ctx, LIBNVME_LOG_ERR, "could not add new controller: %s\n",__libnvme_msg(ctx, LIBNVME_LOG_ERR, ((void*)0), "could not add new controller: %s\n"
, libnvme_strerror(-err))
4332 libnvme_strerror(-err))__libnvme_msg(ctx, LIBNVME_LOG_ERR, ((void*)0), "could not add new controller: %s\n"
, libnvme_strerror(-err))
;
4333 }
4334
4335 libnvme_free_ctrl(c);
4336 return err;
4337 }
4338
4339 write_devid_file(fctx, devid_fd, c);
4340 if (fctx->hooks.connected)
4341 fctx->hooks.connected(fctx, c, fctx->hooks.user_data);
4342
4343 return 0;
4344}