Bug Summary

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

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