Bug Summary

File:.build-ci/../libnvme/src/nvme/fabrics.c
Warning:line 2483, column 18
Although the value stored to 'hid' is used in the enclosing expression, the value is never actually read from 'hid'

Annotated Source Code

Press '?' to see keyboard shortcuts

clang -cc1 -cc1 -triple x86_64-pc-linux-gnu -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/lib/llvm-19/lib/clang/19 -include /__w/nvme-cli/nvme-cli/.build-ci/nvme-config.h -I libnvme/src/libnvme3.so.3.0.0.p -I libnvme/src -I ../libnvme/src -I ccan -I ../ccan -I . -I .. -I /usr/include/json-c -D _FILE_OFFSET_BITS=64 -D _GNU_SOURCE -U NDEBUG -internal-isystem /usr/lib/llvm-19/lib/clang/19/include -internal-isystem /usr/local/include -internal-isystem /usr/lib/gcc/x86_64-linux-gnu/14/../../../../x86_64-linux-gnu/include -internal-externc-isystem /usr/include/x86_64-linux-gnu -internal-externc-isystem /include -internal-externc-isystem /usr/include -O3 -std=gnu99 -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 -D__GCC_HAVE_DWARF2_CFI_ASM=1 -o /__w/nvme-cli/nvme-cli/.build-ci/scan-results/2026-06-24-175442-590-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 <unistd.h>
23
24#include <arpa/inet.h>
25#include <linux1/if_ether.h>
26#include <net/if.h>
27#include <netpacket/packet.h>
28#include <netdb.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 <libnvme.h>
39
40#include "cleanup.h"
41#include "cleanup-linux.h"
42#include "private.h"
43#include "private-fabrics.h"
44#include "compiler-attributes.h"
45
46const char *nvmf_dev = "/dev/nvme-fabrics";
47
48static inline void free_uri(struct libnvmf_uri **uri)
49{
50 libnvmf_uri_free(*uri);
51}
52#define __cleanup_uri__attribute__((cleanup(free_uri))) __cleanup(free_uri)__attribute__((cleanup(free_uri)))
53
54/**
55 * strchomp() - Strip trailing spaces
56 * @str: String to strip
57 * @max: Maximum length of string
58 */
59static void strchomp(char *str, int max)
60{
61 int i;
62
63 for (i = max - 1; i >= 0 && str[i] == ' '; i--) {
64 str[i] = '\0';
65 }
66}
67
68const char *arg_str(const char * const *strings,
69 size_t array_size, size_t idx)
70{
71 if (idx < array_size && strings[idx])
72 return strings[idx];
73 return "unrecognized";
74}
75
76const char * const trtypes[] = {
77 [NVMF_TRTYPE_RDMA] = "rdma",
78 [NVMF_TRTYPE_FC] = "fc",
79 [NVMF_TRTYPE_TCP] = "tcp",
80 [NVMF_TRTYPE_LOOP] = "loop",
81};
82
83__libnvme_public__attribute__((visibility("default"))) const char *libnvmf_trtype_str(__u8 trtype)
84{
85 return arg_str(trtypes, ARRAY_SIZE(trtypes)(sizeof(trtypes) / sizeof((trtypes)[0]) + 0), trtype);
86}
87
88static const char * const adrfams[] = {
89 [NVMF_ADDR_FAMILY_PCI] = "pci",
90 [NVMF_ADDR_FAMILY_IP4] = "ipv4",
91 [NVMF_ADDR_FAMILY_IP6] = "ipv6",
92 [NVMF_ADDR_FAMILY_IB] = "infiniband",
93 [NVMF_ADDR_FAMILY_FC] = "fibre-channel",
94};
95
96__libnvme_public__attribute__((visibility("default"))) const char *libnvmf_adrfam_str(__u8 adrfam)
97{
98 return arg_str(adrfams, ARRAY_SIZE(adrfams)(sizeof(adrfams) / sizeof((adrfams)[0]) + 0), adrfam);
99}
100
101static const char * const subtypes[] = {
102 [NVME_NQN_DISC] = "discovery subsystem referral",
103 [NVME_NQN_NVME] = "nvme subsystem",
104 [NVME_NQN_CURR] = "current discovery subsystem",
105};
106
107__libnvme_public__attribute__((visibility("default"))) const char *libnvmf_subtype_str(__u8 subtype)
108{
109 return arg_str(subtypes, ARRAY_SIZE(subtypes)(sizeof(subtypes) / sizeof((subtypes)[0]) + 0), subtype);
110}
111
112static const char * const treqs[] = {
113 [NVMF_TREQ_NOT_SPECIFIED] = "not specified",
114 [NVMF_TREQ_REQUIRED] = "required",
115 [NVMF_TREQ_NOT_REQUIRED] = "not required",
116 [NVMF_TREQ_NOT_SPECIFIED |
117 NVMF_TREQ_DISABLE_SQFLOW] = "not specified, "
118 "sq flow control disable supported",
119 [NVMF_TREQ_REQUIRED |
120 NVMF_TREQ_DISABLE_SQFLOW] = "required, "
121 "sq flow control disable supported",
122 [NVMF_TREQ_NOT_REQUIRED |
123 NVMF_TREQ_DISABLE_SQFLOW] = "not required, "
124 "sq flow control disable supported",
125};
126
127__libnvme_public__attribute__((visibility("default"))) const char *libnvmf_treq_str(__u8 treq)
128{
129 return arg_str(treqs, ARRAY_SIZE(treqs)(sizeof(treqs) / sizeof((treqs)[0]) + 0), treq);
130}
131
132static const char * const eflags_strings[] = {
133 [NVMF_DISC_EFLAGS_NONE] = "none",
134 [NVMF_DISC_EFLAGS_EPCSD] = "explicit discovery connections",
135 [NVMF_DISC_EFLAGS_DUPRETINFO] = "duplicate discovery information",
136 [NVMF_DISC_EFLAGS_EPCSD |
137 NVMF_DISC_EFLAGS_DUPRETINFO] = "explicit discovery connections, "
138 "duplicate discovery information",
139 [NVMF_DISC_EFLAGS_NCC] = "no cdc connectivity",
140 [NVMF_DISC_EFLAGS_EPCSD |
141 NVMF_DISC_EFLAGS_NCC] = "explicit discovery connections, "
142 "no cdc connectivity",
143 [NVMF_DISC_EFLAGS_DUPRETINFO |
144 NVMF_DISC_EFLAGS_NCC] = "duplicate discovery information, "
145 "no cdc connectivity",
146 [NVMF_DISC_EFLAGS_EPCSD |
147 NVMF_DISC_EFLAGS_DUPRETINFO |
148 NVMF_DISC_EFLAGS_NCC] = "explicit discovery connections, "
149 "duplicate discovery information, "
150 "no cdc connectivity",
151};
152
153__libnvme_public__attribute__((visibility("default"))) const char *libnvmf_eflags_str(__u16 eflags)
154{
155 return arg_str(eflags_strings, ARRAY_SIZE(eflags_strings)(sizeof(eflags_strings) / sizeof((eflags_strings)[0]) + 0), eflags);
156}
157
158static const char * const sectypes[] = {
159 [NVMF_TCP_SECTYPE_NONE] = "none",
160 [NVMF_TCP_SECTYPE_TLS] = "tls",
161 [NVMF_TCP_SECTYPE_TLS13] = "tls13",
162};
163
164__libnvme_public__attribute__((visibility("default"))) const char *libnvmf_sectype_str(__u8 sectype)
165{
166 return arg_str(sectypes, ARRAY_SIZE(sectypes)(sizeof(sectypes) / sizeof((sectypes)[0]) + 0), sectype);
167}
168
169static const char * const prtypes[] = {
170 [NVMF_RDMA_PRTYPE_NOT_SPECIFIED] = "not specified",
171 [NVMF_RDMA_PRTYPE_IB] = "infiniband",
172 [NVMF_RDMA_PRTYPE_ROCE] = "roce",
173 [NVMF_RDMA_PRTYPE_ROCEV2] = "roce-v2",
174 [NVMF_RDMA_PRTYPE_IWARP] = "iwarp",
175};
176
177__libnvme_public__attribute__((visibility("default"))) const char *libnvmf_prtype_str(__u8 prtype)
178{
179 return arg_str(prtypes, ARRAY_SIZE(prtypes)(sizeof(prtypes) / sizeof((prtypes)[0]) + 0), prtype);
180}
181
182static const char * const qptypes[] = {
183 [NVMF_RDMA_QPTYPE_CONNECTED] = "connected",
184 [NVMF_RDMA_QPTYPE_DATAGRAM] = "datagram",
185};
186
187__libnvme_public__attribute__((visibility("default"))) const char *libnvmf_qptype_str(__u8 qptype)
188{
189 return arg_str(qptypes, ARRAY_SIZE(qptypes)(sizeof(qptypes) / sizeof((qptypes)[0]) + 0), qptype);
190}
191
192static const char * const cms[] = {
193 [NVMF_RDMA_CMS_RDMA_CM] = "rdma-cm",
194};
195
196__libnvme_public__attribute__((visibility("default"))) const char *libnvmf_cms_str(__u8 cm)
197{
198 return arg_str(cms, ARRAY_SIZE(cms)(sizeof(cms) / sizeof((cms)[0]) + 0), cm);
199}
200
201void libnvmf_default_config(struct libnvme_fabrics_config *cfg)
202{
203 cfg->tos = -1;
204 cfg->ctrl_loss_tmo = NVMF_DEF_CTRL_LOSS_TMO600;
205}
206
207__libnvme_public__attribute__((visibility("default"))) int libnvmf_context_create(struct libnvme_global_ctx *ctx,
208 bool_Bool (*decide_retry)(struct libnvmf_context *fctx, int err,
209 void *user_data),
210 void (*connected)(struct libnvmf_context *fctx,
211 struct libnvme_ctrl *c, void *user_data),
212 void (*already_connected)(struct libnvmf_context *fctx,
213 struct libnvme_host *host, const char *subsysnqn,
214 const char *transport, const char *traddr,
215 const char *trsvcid, void *user_data),
216 void *user_data, struct libnvmf_context **fctxp)
217{
218 struct libnvmf_context *fctx;
219
220 fctx = calloc(1, sizeof(*fctx));
221 if (!fctx)
222 return -ENOMEM12;
223
224 fctx->ctx = ctx;
225
226 libnvmf_default_config(&fctx->ctrl_params.cfg);
227
228 fctx->hooks.decide_retry = decide_retry;
229 fctx->hooks.connected = connected;
230 fctx->hooks.already_connected = already_connected;
231
232 fctx->hooks.user_data = user_data;
233
234 *fctxp = fctx;
235 return 0;
236}
237
238__libnvme_public__attribute__((visibility("default"))) void libnvmf_context_free(struct libnvmf_context *fctx)
239{
240 if (!fctx)
241 return;
242
243 free(fctx->tls_key);
244 free(fctx);
245}
246
247__libnvme_public__attribute__((visibility("default"))) int libnvmf_context_set_discovery_hooks(
248 struct libnvmf_context *fctx,
249 void (*discovery_log)(struct libnvmf_context *fctx,
250 bool_Bool connect,
251 struct nvmf_discovery_log *log,
252 uint64_t numrec, void *user_data),
253 int (*parser_init)(struct libnvmf_context *fctx,
254 void *user_data),
255 void (*parser_cleanup)(struct libnvmf_context *fctx,
256 void *user_data),
257 int (*parser_next_line)(struct libnvmf_context *fctx,
258 void *user_data))
259{
260 fctx->hooks.discovery_log = discovery_log;
261 fctx->hooks.parser_init = parser_init;
262 fctx->hooks.parser_cleanup = parser_cleanup;
263 fctx->hooks.parser_next_line = parser_next_line;
264
265 return 0;
266}
267
268
269
270__libnvme_public__attribute__((visibility("default"))) int libnvmf_context_set_connection(
271 struct libnvmf_context *fctx, const char *subsysnqn,
272 const char *transport, const char *traddr, const char *trsvcid,
273 const char *host_traddr, const char *host_iface)
274{
275 fctx->ctrl_params.subsysnqn = subsysnqn;
276 fctx->ctrl_params.transport = transport;
277 fctx->ctrl_params.traddr = traddr;
278 fctx->ctrl_params.trsvcid = trsvcid;
279 fctx->ctrl_params.host_traddr = host_traddr;
280 fctx->ctrl_params.host_iface = host_iface;
281
282 return 0;
283}
284
285static const char *hostid_from_hostnqn(const char *hostnqn)
286{
287 const char *match;
288
289 if (!hostnqn)
290 return NULL((void*)0);
291
292 match = strstr(hostnqn, "uuid:");
293 if (!match)
294 return NULL((void*)0);
295
296 return match + strlen("uuid:");
297}
298
299__libnvme_public__attribute__((visibility("default"))) int libnvmf_context_set_hostnqn(struct libnvmf_context *fctx,
300 const char *hostnqn, const char *hostid)
301{
302 fctx->hostnqn = hostnqn;
303 if (!hostid)
304 hostid = hostid_from_hostnqn(hostnqn);
305 fctx->hostid = hostid;
306
307 return 0;
308}
309
310__libnvme_public__attribute__((visibility("default"))) int libnvmf_context_set_crypto(struct libnvmf_context *fctx,
311 const char *hostkey, const char *ctrlkey,
312 const char *keyring, const char *tls_key,
313 const char *tls_key_identity)
314{
315 int err;
316
317 fctx->hostkey = hostkey;
318 fctx->ctrlkey = ctrlkey;
319 fctx->keyring = keyring;
320 fctx->tls_key_identity = tls_key_identity;
321
322 if (!tls_key)
323 return 0;
324
325 if (!strncmp(tls_key, "pin:", 4)) {
326 __cleanup_free__attribute__((cleanup(freep))) unsigned char *raw_secret = NULL((void*)0);
327 __cleanup_free__attribute__((cleanup(freep))) char *encoded_key = NULL((void*)0);
328 int key_len = 32;
329
330 err = libnvme_create_raw_secret(fctx->ctx, tls_key,
331 key_len, &raw_secret);
332 if (err)
333 return err;
334
335 err = libnvme_export_tls_key(fctx->ctx, raw_secret,
336 key_len, &encoded_key);
337 if (err)
338 return err;
339
340 fctx->tls_key = encoded_key;
341 encoded_key = NULL((void*)0);
342 return 0;
343 }
344
345 fctx->tls_key = strdup(tls_key);
346 return 0;
347}
348
349__libnvme_public__attribute__((visibility("default"))) int libnvmf_context_set_device(
350 struct libnvmf_context *fctx, const char *device)
351{
352 fctx->device = device;
353
354 return 0;
355}
356
357__libnvme_public__attribute__((visibility("default"))) int libnvmf_context_set_io_queues(
358 struct libnvmf_context *fctx, int nr_io_queues,
359 int nr_write_queues, int nr_poll_queues,
360 int queue_size, bool_Bool disable_sqflow)
361{
362 fctx->ctrl_params.cfg.nr_io_queues = nr_io_queues;
363 fctx->ctrl_params.cfg.nr_write_queues = nr_write_queues;
364 fctx->ctrl_params.cfg.nr_poll_queues = nr_poll_queues;
365 fctx->ctrl_params.cfg.queue_size = queue_size;
366 fctx->ctrl_params.cfg.disable_sqflow = disable_sqflow;
367
368 return 0;
369}
370
371__libnvme_public__attribute__((visibility("default"))) int libnvmf_context_set_reconnect_policy(
372 struct libnvmf_context *fctx, int ctrl_loss_tmo,
373 int reconnect_delay, int fast_io_fail_tmo)
374{
375 fctx->ctrl_params.cfg.ctrl_loss_tmo = ctrl_loss_tmo;
376 fctx->ctrl_params.cfg.reconnect_delay = reconnect_delay;
377 fctx->ctrl_params.cfg.fast_io_fail_tmo = fast_io_fail_tmo;
378
379 return 0;
380}
381
382/*
383 * Derived from Linux's supported options (the opt_tokens table)
384 * when the mechanism to report supported options was added (f18ee3d988157).
385 * Not all of these options may actually be supported,
386 * but we retain the old behavior of passing all that might be.
387 */
388static const struct libnvme_fabric_options default_supported_options = {
389 .ctrl_loss_tmo = true1,
390 .data_digest = true1,
391 .disable_sqflow = true1,
392 .discovery = true1,
393 .duplicate_connect = true1,
394 .fast_io_fail_tmo = true1,
395 .hdr_digest = true1,
396 .host_iface = true1,
397 .host_traddr = true1,
398 .hostid = true1,
399 .hostnqn = true1,
400 .keep_alive_tmo = true1,
401 .nqn = true1,
402 .nr_io_queues = true1,
403 .nr_poll_queues = true1,
404 .nr_write_queues = true1,
405 .queue_size = true1,
406 .reconnect_delay = true1,
407 .tos = true1,
408 .traddr = true1,
409 .transport = true1,
410 .trsvcid = true1,
411};
412
413#define MERGE_CFG_OPTION(c, n, o, d)if ((c)->o == d) (c)->o = (n)->o \
414 if ((c)->o == d) (c)->o = (n)->o
415static void merge_config(libnvme_ctrl_t c,
416 const struct libnvme_fabrics_config *cfg)
417{
418 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
;
419 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
;
420 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
;
421 MERGE_CFG_OPTION(&c->cfg, cfg, queue_size, 0)if ((&c->cfg)->queue_size == 0) (&c->cfg)->
queue_size = (cfg)->queue_size
;
422 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
;
423 MERGE_CFG_OPTION(&c->cfg, cfg, reconnect_delay, 0)if ((&c->cfg)->reconnect_delay == 0) (&c->cfg
)->reconnect_delay = (cfg)->reconnect_delay
;
424 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
425 NVMF_DEF_CTRL_LOSS_TMO)if ((&c->cfg)->ctrl_loss_tmo == 600) (&c->cfg
)->ctrl_loss_tmo = (cfg)->ctrl_loss_tmo
;
426 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
;
427 MERGE_CFG_OPTION(&c->cfg, cfg, tos, -1)if ((&c->cfg)->tos == -1) (&c->cfg)->tos =
(cfg)->tos
;
428 MERGE_CFG_OPTION(&c->cfg, cfg, keyring_id, 0)if ((&c->cfg)->keyring_id == 0) (&c->cfg)->
keyring_id = (cfg)->keyring_id
;
429 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
;
430 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
;
431 MERGE_CFG_OPTION(&c->cfg, cfg, duplicate_connect, false)if ((&c->cfg)->duplicate_connect == 0) (&c->
cfg)->duplicate_connect = (cfg)->duplicate_connect
;
432 MERGE_CFG_OPTION(&c->cfg, cfg, disable_sqflow, false)if ((&c->cfg)->disable_sqflow == 0) (&c->cfg
)->disable_sqflow = (cfg)->disable_sqflow
;
433 MERGE_CFG_OPTION(&c->cfg, cfg, hdr_digest, false)if ((&c->cfg)->hdr_digest == 0) (&c->cfg)->
hdr_digest = (cfg)->hdr_digest
;
434 MERGE_CFG_OPTION(&c->cfg, cfg, data_digest, false)if ((&c->cfg)->data_digest == 0) (&c->cfg)->
data_digest = (cfg)->data_digest
;
435 MERGE_CFG_OPTION(&c->cfg, cfg, tls, false)if ((&c->cfg)->tls == 0) (&c->cfg)->tls =
(cfg)->tls
;
436 MERGE_CFG_OPTION(&c->cfg, cfg, concat, false)if ((&c->cfg)->concat == 0) (&c->cfg)->concat
= (cfg)->concat
;
437}
438
439#define UPDATE_CFG_OPTION(c, n, o, d)if ((n)->o != d) (c)->o = (n)->o \
440 if ((n)->o != d) (c)->o = (n)->o
441static void update_config(libnvme_ctrl_t c,
442 const struct libnvme_fabrics_config *cfg)
443{
444 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
;
445 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
;
446 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
;
447 UPDATE_CFG_OPTION(&c->cfg, cfg, queue_size, 0)if ((cfg)->queue_size != 0) (&c->cfg)->queue_size
= (cfg)->queue_size
;
448 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
;
449 UPDATE_CFG_OPTION(&c->cfg, cfg, reconnect_delay, 0)if ((cfg)->reconnect_delay != 0) (&c->cfg)->reconnect_delay
= (cfg)->reconnect_delay
;
450 UPDATE_CFG_OPTION(&c->cfg, cfg, ctrl_loss_tmo,if ((cfg)->ctrl_loss_tmo != 600) (&c->cfg)->ctrl_loss_tmo
= (cfg)->ctrl_loss_tmo
451 NVMF_DEF_CTRL_LOSS_TMO)if ((cfg)->ctrl_loss_tmo != 600) (&c->cfg)->ctrl_loss_tmo
= (cfg)->ctrl_loss_tmo
;
452 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
;
453 UPDATE_CFG_OPTION(&c->cfg, cfg, tos, -1)if ((cfg)->tos != -1) (&c->cfg)->tos = (cfg)->
tos
;
454 UPDATE_CFG_OPTION(&c->cfg, cfg, keyring_id, 0)if ((cfg)->keyring_id != 0) (&c->cfg)->keyring_id
= (cfg)->keyring_id
;
455 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
;
456 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
;
457 UPDATE_CFG_OPTION(&c->cfg, cfg, duplicate_connect, false)if ((cfg)->duplicate_connect != 0) (&c->cfg)->duplicate_connect
= (cfg)->duplicate_connect
;
458 UPDATE_CFG_OPTION(&c->cfg, cfg, disable_sqflow, false)if ((cfg)->disable_sqflow != 0) (&c->cfg)->disable_sqflow
= (cfg)->disable_sqflow
;
459 UPDATE_CFG_OPTION(&c->cfg, cfg, hdr_digest, false)if ((cfg)->hdr_digest != 0) (&c->cfg)->hdr_digest
= (cfg)->hdr_digest
;
460 UPDATE_CFG_OPTION(&c->cfg, cfg, data_digest, false)if ((cfg)->data_digest != 0) (&c->cfg)->data_digest
= (cfg)->data_digest
;
461 UPDATE_CFG_OPTION(&c->cfg, cfg, tls, false)if ((cfg)->tls != 0) (&c->cfg)->tls = (cfg)->
tls
;
462 UPDATE_CFG_OPTION(&c->cfg, cfg, concat, false)if ((cfg)->concat != 0) (&c->cfg)->concat = (cfg
)->concat
;
463}
464
465static int __add_bool_argument(char **argstr, char *tok, bool_Bool arg)
466{
467 char *nstr;
468
469 if (!arg)
470 return 0;
471 if (asprintf(&nstr, "%s,%s", *argstr, tok) < 0) {
472 return -ENOMEM12;
473 }
474 free(*argstr);
475 *argstr = nstr;
476
477 return 0;
478}
479
480static int __add_hex_argument(char **argstr, char *tok, int arg, bool_Bool allow_zero)
481{
482 char *nstr;
483
484 if (arg < 0 || (!arg && !allow_zero))
485 return 0;
486 if (asprintf(&nstr, "%s,%s=0x%08x", *argstr, tok, arg) < 0) {
487 return -ENOMEM12;
488 }
489 free(*argstr);
490 *argstr = nstr;
491
492 return 0;
493}
494
495static int __add_int_argument(char **argstr, char *tok, int arg, bool_Bool allow_zero)
496{
497 char *nstr;
498
499 if (arg < 0 || (!arg && !allow_zero))
500 return 0;
501 if (asprintf(&nstr, "%s,%s=%d", *argstr, tok, arg) < 0) {
502 return -ENOMEM12;
503 }
504 free(*argstr);
505 *argstr = nstr;
506
507 return 0;
508}
509
510static int __add_int_or_minus_one_argument(char **argstr, char *tok, int arg)
511{
512 char *nstr;
513
514 if (arg < -1)
515 return 0;
516 if (asprintf(&nstr, "%s,%s=%d", *argstr, tok, arg) < 0) {
517 return -ENOMEM12;
518 }
519 free(*argstr);
520 *argstr = nstr;
521
522 return 0;
523}
524
525static int __add_argument(char **argstr, const char *tok, const char *arg)
526{
527 char *nstr;
528
529 if (!arg || arg[0] == '\0' || !strcmp(arg, "none"))
530 return 0;
531 if (asprintf(&nstr, "%s,%s=%s", *argstr, tok, arg) < 0) {
532 return -ENOMEM12;
533 }
534 free(*argstr);
535 *argstr = nstr;
536
537 return 0;
538}
539
540static int __nvmf_supported_options(struct libnvme_global_ctx *ctx);
541#define nvmf_check_option(ctx, tok)({ !__nvmf_supported_options(ctx) && ctx->options->
tok; })
\
542({ \
543 !__nvmf_supported_options(ctx) && ctx->options->tok; \
544})
545
546#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;
})
\
547({ \
548 int ret; \
549 if (nvmf_check_option(ctx, tok)({ !__nvmf_supported_options(ctx) && ctx->options->
tok; })
) { \
550 ret = __add_bool_argument(argstr, \
551 stringify(tok)"tok", \
552 arg); \
553 } else { \
554 libnvme_msg(ctx, LIBNVME_LOG_DEBUG, \__libnvme_msg(ctx, LIBNVME_LOG_DEBUG, ((void*)0), "option \"%s\" ignored\n"
, "tok")
555 "option \"%s\" ignored\n", \__libnvme_msg(ctx, LIBNVME_LOG_DEBUG, ((void*)0), "option \"%s\" ignored\n"
, "tok")
556 stringify(tok))__libnvme_msg(ctx, LIBNVME_LOG_DEBUG, ((void*)0), "option \"%s\" ignored\n"
, "tok")
; \
557 ret = 0; \
558 } \
559 ret; \
560})
561
562#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
; })
\
563({ \
564 int ret; \
565 if (nvmf_check_option(ctx, tok)({ !__nvmf_supported_options(ctx) && ctx->options->
tok; })
) { \
566 ret = __add_hex_argument(argstr, \
567 stringify(tok)"tok", \
568 arg, \
569 allow_zero); \
570 } else { \
571 libnvme_msg(ctx, LIBNVME_LOG_DEBUG, \__libnvme_msg(ctx, LIBNVME_LOG_DEBUG, ((void*)0), "option \"%s\" ignored\n"
, "tok")
572 "option \"%s\" ignored\n", \__libnvme_msg(ctx, LIBNVME_LOG_DEBUG, ((void*)0), "option \"%s\" ignored\n"
, "tok")
573 stringify(tok))__libnvme_msg(ctx, LIBNVME_LOG_DEBUG, ((void*)0), "option \"%s\" ignored\n"
, "tok")
; \
574 ret = 0; \
575 } \
576 ret; \
577})
578
579#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
; })
\
580({ \
581 int ret; \
582 if (nvmf_check_option(ctx, tok)({ !__nvmf_supported_options(ctx) && ctx->options->
tok; })
) { \
583 ret = __add_int_argument(argstr, \
584 stringify(tok)"tok", \
585 arg, \
586 allow_zero); \
587 } else { \
588 libnvme_msg(ctx, LIBNVME_LOG_DEBUG, \__libnvme_msg(ctx, LIBNVME_LOG_DEBUG, ((void*)0), "option \"%s\" ignored\n"
, "tok")
589 "option \"%s\" ignored\n", \__libnvme_msg(ctx, LIBNVME_LOG_DEBUG, ((void*)0), "option \"%s\" ignored\n"
, "tok")
590 stringify(tok))__libnvme_msg(ctx, LIBNVME_LOG_DEBUG, ((void*)0), "option \"%s\" ignored\n"
, "tok")
; \
591 ret = 0; \
592 } \
593 ret; \
594})
595
596#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
; })
\
597({ \
598 int ret; \
599 if (nvmf_check_option(ctx, tok)({ !__nvmf_supported_options(ctx) && ctx->options->
tok; })
) { \
600 ret = __add_int_or_minus_one_argument(argstr, \
601 stringify(tok)"tok", \
602 arg); \
603 } else { \
604 libnvme_msg(ctx, LIBNVME_LOG_DEBUG, \__libnvme_msg(ctx, LIBNVME_LOG_DEBUG, ((void*)0), "option \"%s\" ignored\n"
, "tok")
605 "option \"%s\" ignored\n", \__libnvme_msg(ctx, LIBNVME_LOG_DEBUG, ((void*)0), "option \"%s\" ignored\n"
, "tok")
606 stringify(tok))__libnvme_msg(ctx, LIBNVME_LOG_DEBUG, ((void*)0), "option \"%s\" ignored\n"
, "tok")
; \
607 ret = 0; \
608 } \
609 ret; \
610})
611
612#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; })
\
613({ \
614 int ret; \
615 if (nvmf_check_option(ctx, tok)({ !__nvmf_supported_options(ctx) && ctx->options->
tok; })
) { \
616 ret = __add_argument(argstr, \
617 stringify(tok)"tok", \
618 arg); \
619 } else { \
620 libnvme_msg(ctx, LIBNVME_LOG_WARN, \__libnvme_msg(ctx, LIBNVME_LOG_WARN, ((void*)0), "option \"%s\" ignored\n"
, "tok")
621 "option \"%s\" ignored\n", \__libnvme_msg(ctx, LIBNVME_LOG_WARN, ((void*)0), "option \"%s\" ignored\n"
, "tok")
622 stringify(tok))__libnvme_msg(ctx, LIBNVME_LOG_WARN, ((void*)0), "option \"%s\" ignored\n"
, "tok")
; \
623 ret = 0; \
624 } \
625 ret; \
626})
627
628static int inet4_pton(const char *src, uint16_t port,
629 struct sockaddr_storage *addr)
630{
631 struct sockaddr_in *addr4 = (struct sockaddr_in *)addr;
632
633 if (strlen(src) > INET_ADDRSTRLEN16)
634 return -EINVAL22;
635
636 if (inet_pton(AF_INET2, src, &addr4->sin_addr.s_addr) <= 0)
637 return -EINVAL22;
638
639 addr4->sin_family = AF_INET2;
640 addr4->sin_port = htons(port)__bswap_16 (port);
641
642 return 0;
643}
644
645static int inet6_pton(struct libnvme_global_ctx *ctx, const char *src, uint16_t port,
646 struct sockaddr_storage *addr)
647{
648 struct sockaddr_in6 *addr6 = (struct sockaddr_in6 *)addr;
649 const char *scope = NULL((void*)0);
650 char *p;
651
652 if (strlen(src) > INET6_ADDRSTRLEN46)
653 return -EINVAL22;
654
655 __cleanup_free__attribute__((cleanup(freep))) char *tmp = strdup(src);
656 if (!tmp) {
657 libnvme_msg(ctx, LIBNVME_LOG_ERR, "cannot copy: %s\n", src)__libnvme_msg(ctx, LIBNVME_LOG_ERR, ((void*)0), "cannot copy: %s\n"
, src)
;
658 return -ENOMEM12;
659 }
660
661 p = strchr(tmp, '%');
662 if (p) {
663 *p = '\0';
664 scope = src + (p - tmp) + 1;
665 }
666
667 if (inet_pton(AF_INET610, tmp, &addr6->sin6_addr) != 1)
668 return -EINVAL22;
669
670 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) {
671 addr6->sin6_scope_id = if_nametoindex(scope);
672 if (addr6->sin6_scope_id == 0) {
673 libnvme_msg(ctx, LIBNVME_LOG_ERR,__libnvme_msg(ctx, LIBNVME_LOG_ERR, ((void*)0), "can't find iface index for: %s (%m)\n"
, scope)
674 "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)
;
675 return -EINVAL22;
676 }
677 }
678
679 addr6->sin6_family = AF_INET610;
680 addr6->sin6_port = htons(port)__bswap_16 (port);
681 return 0;
682}
683
684/**
685 * inet_pton_with_scope - convert an IPv4/IPv6 to socket address
686 * @ctx: Global context
687 * @af: address family, AF_INET, AF_INET6 or AF_UNSPEC for either
688 * @src: the start of the address string
689 * @trsvcid: transport service identifier
690 * @addr: output socket address
691 *
692 * Return 0 on success, errno otherwise.
693 */
694static int inet_pton_with_scope(struct libnvme_global_ctx *ctx, int af,
695 const char *src, const char * trsvcid,
696 struct sockaddr_storage *addr)
697{
698 int ret = -EINVAL22;
699 uint16_t port = 0;
700
701 if (trsvcid) {
702 unsigned long long tmp = strtoull(trsvcid, NULL((void*)0), 0);
703 port = (uint16_t)tmp;
704 if (tmp != port) {
705 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)
706 trsvcid)__libnvme_msg(ctx, LIBNVME_LOG_ERR, ((void*)0), "trsvcid out of range: %s\n"
, trsvcid)
;
707 return -ERANGE34;
708 }
709 } else {
710 port = 0;
711 }
712
713 switch (af) {
714 case AF_INET2:
715 ret = inet4_pton(src, port, addr);
716 break;
717 case AF_INET610:
718 ret = inet6_pton(ctx, src, port, addr);
719 break;
720 case AF_UNSPEC0:
721 ret = inet4_pton(src, port, addr);
722 if (ret)
723 ret = inet6_pton(ctx, src, port, addr);
724 break;
725 default:
726 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)
;
727 }
728
729 return ret;
730}
731
732bool_Bool traddr_is_hostname(struct libnvme_global_ctx *ctx,
733 const char *transport, const char *traddr)
734{
735 struct sockaddr_storage addr;
736
737 if (!traddr || !transport)
738 return false0;
739 if (!strcmp(traddr, "none"))
740 return false0;
741 if (strcmp(transport, "tcp") && strcmp(transport, "rdma"))
742 return false0;
743 if (inet_pton_with_scope(ctx, AF_UNSPEC0,
744 traddr, NULL((void*)0), &addr) == 0) /* scope-aware */
745 return false0;
746
747 return true1;
748}
749
750static int build_options(libnvme_host_t h, libnvme_ctrl_t c, char **argstr)
751{
752 const char *transport = libnvme_ctrl_get_transport(c);
753 const char *hostnqn, *hostid, *hostkey, *ctrlkey = NULL((void*)0);
754 bool_Bool discover = false0, discovery_nqn = false0;
755 struct libnvme_global_ctx *ctx = h->ctx;
756 long keyring_id = 0;
757 long key_id = 0;
758 int ret;
759
760 if (!transport) {
761 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"
)
;
762 return -ENVME_CONNECT_TARG;
763 }
764
765 if (strncmp(transport, "loop", 4)) {
766 if (!libnvme_ctrl_get_traddr(c)) {
767 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"
)
;
768 return -ENVME_CONNECT_AARG;
769 }
770 }
771
772 /* always specify nqn as first arg - this will init the string */
773 if (asprintf(argstr, "nqn=%s",
774 libnvme_ctrl_get_subsysnqn(c)) < 0) {
775 return -ENOMEM12;
776 }
777
778 if (!strcmp(libnvme_ctrl_get_subsysnqn(c), NVME_DISC_SUBSYS_NAME"nqn.2014-08.org.nvmexpress.discovery")) {
779 libnvme_ctrl_set_discovery_ctrl(c, true1);
780 libnvme_ctrl_set_unique_discovery_ctrl(c, false0);
781 discovery_nqn = true1;
782 }
783
784 if (libnvme_ctrl_get_discovery_ctrl(c))
785 discover = true1;
786
787 hostnqn = libnvme_host_get_hostnqn(h);
788 hostid = libnvme_host_get_hostid(h);
789 hostkey = libnvme_host_get_dhchap_host_key(h);
790 if (!hostkey)
791 hostkey = libnvme_ctrl_get_dhchap_host_key(c);
792
793 if (hostkey)
794 ctrlkey = libnvme_ctrl_get_dhchap_ctrl_key(c);
795
796 if (c->cfg.tls && c->cfg.concat) {
797 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"
)
;
798 return -ENVME_CONNECT_INVAL;
799 }
800
801 if (c->cfg.concat && !hostkey) {
802 libnvme_msg(h->ctx, LIBNVME_LOG_ERR, "required argument [--dhchap-secret | -S] not specified with --concat\n")__libnvme_msg(h->ctx, LIBNVME_LOG_ERR, ((void*)0), "required argument [--dhchap-secret | -S] not specified with --concat\n"
)
;
803 return -ENVME_CONNECT_INVAL;
804 }
805
806 if (c->cfg.tls) {
807 ret = __libnvme_import_keys_from_config(h, c,
808 &keyring_id, &key_id);
809 if (ret)
810 return ret;
811
812 if (key_id == 0) {
813 if (c->cfg.tls_configured_key_id)
814 key_id = c->cfg.tls_configured_key_id;
815 else
816 key_id = c->cfg.tls_key_id;
817 }
818 }
819
820 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; })
||
821 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; })
822 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; })
||
823 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; })
824 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; })
||
825 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; })
826 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; })
||
827 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; })
828 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; })
||
829 (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; }
)
) ||
830 (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; }
)
) ||
831 (discover && !discovery_nqn &&
832 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; })
) ||
833 (hostkey &&
834 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; })
) ||
835 (ctrlkey &&
836 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; })
) ||
837 (!discover &&
838 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; })
839 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; })
) ||
840 (!discover &&
841 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; })
842 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; })
) ||
843 (!discover &&
844 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; })
845 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; })
) ||
846 (!discover &&
847 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; })
848 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; })
) ||
849 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; })
850 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; })
||
851 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; })
852 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; })
||
853 (strcmp(transport, "loop") &&
854 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; })
855 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; })
) ||
856 (strcmp(transport, "loop") &&
857 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; })
858 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; })
) ||
859 (strcmp(transport, "loop") &&
860 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
; })
) ||
861 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; })
||
862 (!strcmp(transport, "tcp") &&
863 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; })
) ||
864 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; })
865 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; })
||
866 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; })
867 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; })
||
868 (!strcmp(transport, "tcp") &&
869 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; })
) ||
870 (!strcmp(transport, "tcp") &&
871 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; })
) ||
872 (!strcmp(transport, "tcp") &&
873 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
; })
) ||
874 (!strcmp(transport, "tcp") &&
875 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; })
)) {
876 free(*argstr);
877 return -1;
878 }
879
880 return 0;
881}
882
883#define parse_option(ctx, v, name)if (!strcmp(v, "name")) { ctx->options->name = 1; continue
; }
\
884 if (!strcmp(v, stringify(name)"name")) { \
885 ctx->options->name = true1; \
886 continue; \
887 }
888
889static int __nvmf_supported_options(struct libnvme_global_ctx *ctx)
890{
891 char buf[0x1000], *options, *p, *v;
892 __cleanup_fd__attribute__((cleanup(cleanup_fd))) int fd = -1;
893 ssize_t len;
894
895 if (ctx->options)
896 return 0;
897
898 ctx->options = calloc(1, sizeof(*ctx->options));
899 if (!ctx->options)
900 return -ENOMEM12;
901
902 fd = open(nvmf_dev, O_RDONLY00);
903 if (fd < 0) {
904 libnvme_msg(ctx, LIBNVME_LOG_ERR, "Failed to open %s: %s\n",__libnvme_msg(ctx, LIBNVME_LOG_ERR, ((void*)0), "Failed to open %s: %s\n"
, nvmf_dev, libnvme_strerror((*__errno_location ())))
905 nvmf_dev, libnvme_strerror(errno))__libnvme_msg(ctx, LIBNVME_LOG_ERR, ((void*)0), "Failed to open %s: %s\n"
, nvmf_dev, libnvme_strerror((*__errno_location ())))
;
906 return -ENVME_CONNECT_OPEN;
907 }
908
909 memset(buf, 0x0, sizeof(buf));
910 len = read(fd, buf, sizeof(buf) - 1);
911 if (len < 0) {
912 if (errno(*__errno_location ()) == EINVAL22) {
913 /*
914 * Older Linux kernels don't allow reading from nvmf_dev
915 * to get supported options, so use a default set
916 */
917 libnvme_msg(ctx, LIBNVME_LOG_DEBUG,__libnvme_msg(ctx, LIBNVME_LOG_DEBUG, ((void*)0), "Cannot read %s, using default options\n"
, nvmf_dev)
918 "Cannot read %s, using default options\n",__libnvme_msg(ctx, LIBNVME_LOG_DEBUG, ((void*)0), "Cannot read %s, using default options\n"
, nvmf_dev)
919 nvmf_dev)__libnvme_msg(ctx, LIBNVME_LOG_DEBUG, ((void*)0), "Cannot read %s, using default options\n"
, nvmf_dev)
;
920 *ctx->options = default_supported_options;
921 return 0;
922 }
923
924 libnvme_msg(ctx, LIBNVME_LOG_ERR, "Failed to read from %s: %s\n",__libnvme_msg(ctx, LIBNVME_LOG_ERR, ((void*)0), "Failed to read from %s: %s\n"
, nvmf_dev, libnvme_strerror((*__errno_location ())))
925 nvmf_dev, libnvme_strerror(errno))__libnvme_msg(ctx, LIBNVME_LOG_ERR, ((void*)0), "Failed to read from %s: %s\n"
, nvmf_dev, libnvme_strerror((*__errno_location ())))
;
926 return -ENVME_CONNECT_READ;
927 }
928
929 buf[len] = '\0';
930 options = buf;
931
932 libnvme_msg(ctx, LIBNVME_LOG_DEBUG, "kernel supports: ")__libnvme_msg(ctx, LIBNVME_LOG_DEBUG, ((void*)0), "kernel supports: "
)
;
933
934 while ((p = strsep(&options, ",\n")) != NULL((void*)0)) {
935 if (!*p)
936 continue;
937 v = strsep(&p, "= ");
938 if (!v)
939 continue;
940 libnvme_msg(ctx, LIBNVME_LOG_DEBUG, "%s ", v)__libnvme_msg(ctx, LIBNVME_LOG_DEBUG, ((void*)0), "%s ", v);
941
942 parse_option(ctx, v, cntlid)if (!strcmp(v, "cntlid")) { ctx->options->cntlid = 1; continue
; }
;
943 parse_option(ctx, v, concat)if (!strcmp(v, "concat")) { ctx->options->concat = 1; continue
; }
;
944 parse_option(ctx, v, ctrl_loss_tmo)if (!strcmp(v, "ctrl_loss_tmo")) { ctx->options->ctrl_loss_tmo
= 1; continue; }
;
945 parse_option(ctx, v, data_digest)if (!strcmp(v, "data_digest")) { ctx->options->data_digest
= 1; continue; }
;
946 parse_option(ctx, v, dhchap_ctrl_secret)if (!strcmp(v, "dhchap_ctrl_secret")) { ctx->options->dhchap_ctrl_secret
= 1; continue; }
;
947 parse_option(ctx, v, dhchap_secret)if (!strcmp(v, "dhchap_secret")) { ctx->options->dhchap_secret
= 1; continue; }
;
948 parse_option(ctx, v, disable_sqflow)if (!strcmp(v, "disable_sqflow")) { ctx->options->disable_sqflow
= 1; continue; }
;
949 parse_option(ctx, v, discovery)if (!strcmp(v, "discovery")) { ctx->options->discovery =
1; continue; }
;
950 parse_option(ctx, v, duplicate_connect)if (!strcmp(v, "duplicate_connect")) { ctx->options->duplicate_connect
= 1; continue; }
;
951 parse_option(ctx, v, fast_io_fail_tmo)if (!strcmp(v, "fast_io_fail_tmo")) { ctx->options->fast_io_fail_tmo
= 1; continue; }
;
952 parse_option(ctx, v, hdr_digest)if (!strcmp(v, "hdr_digest")) { ctx->options->hdr_digest
= 1; continue; }
;
953 parse_option(ctx, v, host_iface)if (!strcmp(v, "host_iface")) { ctx->options->host_iface
= 1; continue; }
;
954 parse_option(ctx, v, host_traddr)if (!strcmp(v, "host_traddr")) { ctx->options->host_traddr
= 1; continue; }
;
955 parse_option(ctx, v, hostid)if (!strcmp(v, "hostid")) { ctx->options->hostid = 1; continue
; }
;
956 parse_option(ctx, v, hostnqn)if (!strcmp(v, "hostnqn")) { ctx->options->hostnqn = 1;
continue; }
;
957 parse_option(ctx, v, instance)if (!strcmp(v, "instance")) { ctx->options->instance = 1
; continue; }
;
958 parse_option(ctx, v, keep_alive_tmo)if (!strcmp(v, "keep_alive_tmo")) { ctx->options->keep_alive_tmo
= 1; continue; }
;
959 parse_option(ctx, v, keyring)if (!strcmp(v, "keyring")) { ctx->options->keyring = 1;
continue; }
;
960 parse_option(ctx, v, nqn)if (!strcmp(v, "nqn")) { ctx->options->nqn = 1; continue
; }
;
961 parse_option(ctx, v, nr_io_queues)if (!strcmp(v, "nr_io_queues")) { ctx->options->nr_io_queues
= 1; continue; }
;
962 parse_option(ctx, v, nr_poll_queues)if (!strcmp(v, "nr_poll_queues")) { ctx->options->nr_poll_queues
= 1; continue; }
;
963 parse_option(ctx, v, nr_write_queues)if (!strcmp(v, "nr_write_queues")) { ctx->options->nr_write_queues
= 1; continue; }
;
964 parse_option(ctx, v, queue_size)if (!strcmp(v, "queue_size")) { ctx->options->queue_size
= 1; continue; }
;
965 parse_option(ctx, v, reconnect_delay)if (!strcmp(v, "reconnect_delay")) { ctx->options->reconnect_delay
= 1; continue; }
;
966 parse_option(ctx, v, tls)if (!strcmp(v, "tls")) { ctx->options->tls = 1; continue
; }
;
967 parse_option(ctx, v, tls_key)if (!strcmp(v, "tls_key")) { ctx->options->tls_key = 1;
continue; }
;
968 parse_option(ctx, v, tos)if (!strcmp(v, "tos")) { ctx->options->tos = 1; continue
; }
;
969 parse_option(ctx, v, traddr)if (!strcmp(v, "traddr")) { ctx->options->traddr = 1; continue
; }
;
970 parse_option(ctx, v, transport)if (!strcmp(v, "transport")) { ctx->options->transport =
1; continue; }
;
971 parse_option(ctx, v, trsvcid)if (!strcmp(v, "trsvcid")) { ctx->options->trsvcid = 1;
continue; }
;
972 }
973 libnvme_msg(ctx, LIBNVME_LOG_DEBUG, "\n")__libnvme_msg(ctx, LIBNVME_LOG_DEBUG, ((void*)0), "\n");
974 return 0;
975}
976
977/*
978 * Best-effort registry update after a successful connect: record ownership
979 * when an owner is set, otherwise clear any stale entry for a recycled
980 * instance. Failures are logged but never fail the connection.
981 */
982static void registry_update_on_connect(struct libnvme_global_ctx *ctx,
983 int instance)
984{
985 int ret;
986
987 if (ctx->owner)
988 ret = libnvmf_registry_create_instance(ctx, instance,
989 ctx->owner);
990 else
991 ret = libnvmf_registry_delete_instance(ctx, instance);
992 if (ret)
993 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))
994 "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))
995 instance, libnvme_strerror(-ret))__libnvme_msg(ctx, LIBNVME_LOG_WARN, ((void*)0), "nvme%d: registry update failed: %s\n"
, instance, libnvme_strerror(-ret))
;
996}
997
998static int __nvmf_add_ctrl(struct libnvme_global_ctx *ctx, const char *argstr)
999{
1000 __cleanup_fd__attribute__((cleanup(cleanup_fd))) int fd = -1;
1001 int ret, len = strlen(argstr);
1002 int instance;
1003 char buf[0x1000], *options, *p;
1004
1005 fd = open(nvmf_dev, O_RDWR02);
1006 if (fd < 0) {
1007 libnvme_msg(ctx, LIBNVME_LOG_ERR, "Failed to open %s: %s\n",__libnvme_msg(ctx, LIBNVME_LOG_ERR, ((void*)0), "Failed to open %s: %s\n"
, nvmf_dev, libnvme_strerror((*__errno_location ())))
1008 nvmf_dev, libnvme_strerror(errno))__libnvme_msg(ctx, LIBNVME_LOG_ERR, ((void*)0), "Failed to open %s: %s\n"
, nvmf_dev, libnvme_strerror((*__errno_location ())))
;
1009 return -ENVME_CONNECT_OPEN;
1010 }
1011
1012 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)
1013 (int)strcspn(argstr,"\n"), argstr)__libnvme_msg(ctx, LIBNVME_LOG_DEBUG, ((void*)0), "connect ctrl, '%.*s'\n"
, (int)strcspn(argstr,"\n"), argstr)
;
1014 ret = write(fd, argstr, len);
1015 if (ret != len) {
1016 libnvme_msg(ctx, LIBNVME_LOG_INFO, "Failed to write to %s: %s\n",__libnvme_msg(ctx, LIBNVME_LOG_INFO, ((void*)0), "Failed to write to %s: %s\n"
, nvmf_dev, libnvme_strerror((*__errno_location ())))
1017 nvmf_dev, libnvme_strerror(errno))__libnvme_msg(ctx, LIBNVME_LOG_INFO, ((void*)0), "Failed to write to %s: %s\n"
, nvmf_dev, libnvme_strerror((*__errno_location ())))
;
1018 switch (errno(*__errno_location ())) {
1019 case EALREADY114:
1020 return -ENVME_CONNECT_ALREADY;
1021 case EINVAL22:
1022 return -ENVME_CONNECT_INVAL;
1023 case EADDRINUSE98:
1024 return -ENVME_CONNECT_ADDRINUSE;
1025 case ENODEV19:
1026 return -ENVME_CONNECT_NODEV;
1027 case EOPNOTSUPP95:
1028 return -ENVME_CONNECT_OPNOTSUPP;
1029 case ECONNREFUSED111:
1030 return -ENVME_CONNECT_CONNREFUSED;
1031 case EADDRNOTAVAIL99:
1032 return -ENVME_CONNECT_ADDRNOTAVAIL;
1033 case ENOKEY126:
1034 return -ENVME_CONNECT_NOKEY;
1035 default:
1036 return -ENVME_CONNECT_WRITE;
1037 }
1038 }
1039
1040 memset(buf, 0x0, sizeof(buf));
1041 len = read(fd, buf, sizeof(buf) - 1);
1042 if (len < 0) {
1043 libnvme_msg(ctx, LIBNVME_LOG_ERR, "Failed to read from %s: %s\n",__libnvme_msg(ctx, LIBNVME_LOG_ERR, ((void*)0), "Failed to read from %s: %s\n"
, nvmf_dev, libnvme_strerror((*__errno_location ())))
1044 nvmf_dev, libnvme_strerror(errno))__libnvme_msg(ctx, LIBNVME_LOG_ERR, ((void*)0), "Failed to read from %s: %s\n"
, nvmf_dev, libnvme_strerror((*__errno_location ())))
;
1045 return -ENVME_CONNECT_READ;
1046 }
1047 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)
1048 (int)strcspn(buf, "\n"), buf)__libnvme_msg(ctx, LIBNVME_LOG_DEBUG, ((void*)0), "connect ctrl, response '%.*s'\n"
, (int)strcspn(buf, "\n"), buf)
;
1049 buf[len] = '\0';
1050 options = buf;
1051 while ((p = strsep(&options, ",\n")) != NULL((void*)0)) {
1052 if (!*p)
1053 continue;
1054 if (sscanf(p, "instance=%d", &instance) == 1) {
1055 registry_update_on_connect(ctx, instance);
1056 return instance;
1057 }
1058 }
1059
1060 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)
;
1061 return -ENVME_CONNECT_PARSE;
1062}
1063
1064
1065__libnvme_public__attribute__((visibility("default"))) int libnvmf_create_ctrl(struct libnvme_global_ctx *ctx,
1066 struct libnvmf_context *fctx, libnvme_ctrl_t *cp)
1067{
1068 return libnvme_create_ctrl(ctx, &fctx->ctrl_params, cp);
1069}
1070
1071__libnvme_public__attribute__((visibility("default"))) int libnvmf_add_ctrl(libnvme_host_t h, libnvme_ctrl_t c)
1072{
1073 libnvme_subsystem_t s;
1074 __cleanup_free__attribute__((cleanup(freep))) char *argstr = NULL((void*)0);
1075 int ret;
1076
1077 /* Are duplicate connections allowed on existing controller */
1078 if (libnvme_ctrl_get_name(c) && !c->cfg.duplicate_connect)
1079 return -ENVME_CONNECT_ALREADY;
1080
1081 /* apply configuration from config file (JSON) */
1082 s = libnvme_lookup_subsystem(h, NULL((void*)0), libnvme_ctrl_get_subsysnqn(c));
1083 if (s) {
1084 libnvme_ctrl_t fc;
1085 struct libnvmf_context fctx = {
1086 .ctrl_params = {
1087 .transport = libnvme_ctrl_get_transport(c),
1088 .traddr = libnvme_ctrl_get_traddr(c),
1089 .host_traddr = libnvme_ctrl_get_host_traddr(c),
1090 .host_iface = libnvme_ctrl_get_trsvcid(c),
1091 .trsvcid = libnvme_ctrl_get_trsvcid(c),
1092 },
1093 };
1094
1095 fc = libnvmf_ctrl_find(s, &fctx);
1096 if (fc) {
1097 const char *key;
1098
1099 merge_config(c, &fc->cfg);
1100 /*
1101 * An authentication key might already been set
1102 * in @cfg, so ensure to update @c with the correct
1103 * controller key.
1104 */
1105 key = libnvme_ctrl_get_dhchap_host_key(fc);
1106 if (key)
1107 libnvme_ctrl_set_dhchap_host_key(c, key);
1108 key = libnvme_ctrl_get_dhchap_ctrl_key(fc);
1109 if (key)
1110 libnvme_ctrl_set_dhchap_ctrl_key(c, key);
1111 key = libnvme_ctrl_get_keyring(fc);
1112 if (key)
1113 libnvme_ctrl_set_keyring(c, key);
1114 key = libnvme_ctrl_get_tls_key_identity(fc);
1115 if (key)
1116 libnvme_ctrl_set_tls_key_identity(c, key);
1117 key = libnvme_ctrl_get_tls_key(fc);
1118 if (key)
1119 libnvme_ctrl_set_tls_key(c, key);
1120 }
1121
1122 }
1123
1124 libnvme_ctrl_set_discovered(c, true1);
1125 if (traddr_is_hostname(h->ctx, c->transport, c->traddr)) {
1126 char *traddr = c->traddr;
1127
1128 if (hostname2traddr(h->ctx, traddr, &c->traddr)) {
1129 c->traddr = traddr;
1130 return -ENVME_CONNECT_TRADDR;
1131 }
1132 free(traddr);
1133 }
1134
1135 ret = build_options(h, c, &argstr);
1136 if (ret)
1137 return ret;
1138
1139 ret = __nvmf_add_ctrl(h->ctx, argstr);
1140 if (ret < 0)
1141 return ret;
1142
1143 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))
1144 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))
;
1145 return libnvme_init_ctrl(h, c, ret);
1146}
1147
1148__libnvme_public__attribute__((visibility("default"))) int libnvmf_connect_ctrl(libnvme_ctrl_t c)
1149{
1150 __cleanup_free__attribute__((cleanup(freep))) char *argstr = NULL((void*)0);
1151 int ret;
1152
1153 ret = build_options(c->s->h, c, &argstr);
1154 if (ret)
1155 return ret;
1156
1157 ret = __nvmf_add_ctrl(c->s->h->ctx, argstr);
1158 if (ret < 0)
1159 return ret;
1160
1161 return 0;
1162}
1163
1164__libnvme_public__attribute__((visibility("default"))) int libnvmf_disconnect_ctrl(libnvme_ctrl_t c)
1165{
1166 struct libnvme_global_ctx *ctx = c->s && c->s->h ? c->s->h->ctx : NULL((void*)0);
1167 int ret;
1168
1169 ret = libnvme_set_attr(libnvme_ctrl_get_sysfs_dir(c),
1170 "delete_controller", "1");
1171 if (ret < 0) {
1172 libnvme_msg(ctx, LIBNVME_LOG_ERR,__libnvme_msg(ctx, LIBNVME_LOG_ERR, ((void*)0), "%s: failed to disconnect, error %d\n"
, c->name, (*__errno_location ()))
1173 "%s: failed to disconnect, error %d\n", c->name, errno)__libnvme_msg(ctx, LIBNVME_LOG_ERR, ((void*)0), "%s: failed to disconnect, error %d\n"
, c->name, (*__errno_location ()))
;
1174 return ret;
1175 }
1176 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)
1177 c->name, c->subsysnqn)__libnvme_msg(ctx, LIBNVME_LOG_INFO, ((void*)0), "%s: %s disconnected\n"
, c->name, c->subsysnqn)
;
1178 nvme_deconfigure_ctrl(c);
1179 return 0;
1180}
1181
1182static void nvmf_update_tls_concat(struct nvmf_disc_log_entry *e,
1183 libnvme_ctrl_t c, libnvme_host_t h)
1184{
1185 if (!e)
1186 return;
1187
1188 if (e->trtype != NVMF_TRTYPE_TCP ||
1189 e->tsas.tcp.sectype == NVMF_TCP_SECTYPE_NONE)
1190 return;
1191
1192 if (e->treq & NVMF_TREQ_REQUIRED) {
1193 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))
1194 "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))
1195 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))
1196 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))
;
1197
1198 c->cfg.tls = true1;
1199 return;
1200 }
1201
1202 if (e->treq & NVMF_TREQ_NOT_REQUIRED) {
1203 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))
1204 "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))
1205 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))
1206 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))
;
1207
1208 c->cfg.concat = true1;
1209 return;
1210 }
1211}
1212
1213static int nvmf_connect_disc_entry(libnvme_host_t h,
1214 struct nvmf_disc_log_entry *e,
1215 struct libnvmf_context *fctx,
1216 bool_Bool *discover, libnvme_ctrl_t *cp)
1217{
1218 libnvme_ctrl_t c;
1219 int ret;
1220
1221 switch (e->trtype) {
1222 case NVMF_TRTYPE_RDMA:
1223 case NVMF_TRTYPE_TCP:
1224 switch (e->adrfam) {
1225 case NVMF_ADDR_FAMILY_IP4:
1226 case NVMF_ADDR_FAMILY_IP6:
1227 fctx->ctrl_params.traddr = e->traddr;
1228 fctx->ctrl_params.trsvcid = e->trsvcid;
1229 break;
1230 default:
1231 libnvme_msg(h->ctx, LIBNVME_LOG_ERR,__libnvme_msg(h->ctx, LIBNVME_LOG_ERR, ((void*)0), "skipping unsupported adrfam %d\n"
, e->adrfam)
1232 "skipping unsupported adrfam %d\n",__libnvme_msg(h->ctx, LIBNVME_LOG_ERR, ((void*)0), "skipping unsupported adrfam %d\n"
, e->adrfam)
1233 e->adrfam)__libnvme_msg(h->ctx, LIBNVME_LOG_ERR, ((void*)0), "skipping unsupported adrfam %d\n"
, e->adrfam)
;
1234 return -EINVAL22;
1235 }
1236 break;
1237 case NVMF_TRTYPE_FC:
1238 switch (e->adrfam) {
1239 case NVMF_ADDR_FAMILY_FC:
1240 fctx->ctrl_params.traddr = e->traddr;
1241 break;
1242 default:
1243 libnvme_msg(h->ctx, LIBNVME_LOG_ERR,__libnvme_msg(h->ctx, LIBNVME_LOG_ERR, ((void*)0), "skipping unsupported adrfam %d\n"
, e->adrfam)
1244 "skipping unsupported adrfam %d\n",__libnvme_msg(h->ctx, LIBNVME_LOG_ERR, ((void*)0), "skipping unsupported adrfam %d\n"
, e->adrfam)
1245 e->adrfam)__libnvme_msg(h->ctx, LIBNVME_LOG_ERR, ((void*)0), "skipping unsupported adrfam %d\n"
, e->adrfam)
;
1246 return -EINVAL22;
1247 }
1248 break;
1249 case NVMF_TRTYPE_LOOP:
1250 fctx->ctrl_params.traddr = strlen(e->traddr) ? e->traddr : NULL((void*)0);
1251 break;
1252 default:
1253 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)
1254 e->trtype)__libnvme_msg(h->ctx, LIBNVME_LOG_ERR, ((void*)0), "skipping unsupported transport %d\n"
, e->trtype)
;
1255 return -EINVAL22;
1256 }
1257
1258 fctx->ctrl_params.transport = libnvmf_trtype_str(e->trtype);
1259 fctx->ctrl_params.subsysnqn = e->subnqn;
1260
1261 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"
, fctx->ctrl_params.transport, fctx->ctrl_params.traddr
, fctx->ctrl_params.trsvcid)
1262 "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"
, fctx->ctrl_params.transport, fctx->ctrl_params.traddr
, fctx->ctrl_params.trsvcid)
1263 fctx->ctrl_params.transport, fctx->ctrl_params.traddr,__libnvme_msg(h->ctx, LIBNVME_LOG_DEBUG, ((void*)0), "lookup ctrl (transport: %s, traddr: %s, trsvcid %s)\n"
, fctx->ctrl_params.transport, fctx->ctrl_params.traddr
, fctx->ctrl_params.trsvcid)
1264 fctx->ctrl_params.trsvcid)__libnvme_msg(h->ctx, LIBNVME_LOG_DEBUG, ((void*)0), "lookup ctrl (transport: %s, traddr: %s, trsvcid %s)\n"
, fctx->ctrl_params.transport, fctx->ctrl_params.traddr
, fctx->ctrl_params.trsvcid)
;
1265
1266 ret = libnvme_create_ctrl(h->ctx, &fctx->ctrl_params, &c);
1267 if (ret) {
1268 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", fctx->
ctrl_params.transport, fctx->ctrl_params.traddr)
1269 "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", fctx->
ctrl_params.transport, fctx->ctrl_params.traddr)
1270 fctx->ctrl_params.transport, fctx->ctrl_params.traddr)__libnvme_msg(h->ctx, LIBNVME_LOG_DEBUG, ((void*)0), "skipping discovery entry, "
"failed to allocate %s controller with traddr %s\n", fctx->
ctrl_params.transport, fctx->ctrl_params.traddr)
;
1271 return ret;
1272 }
1273
1274 switch (e->subtype) {
1275 case NVME_NQN_CURR:
1276 libnvme_ctrl_set_discovered(c, true1);
1277 libnvme_ctrl_set_unique_discovery_ctrl(c,
1278 strcmp(e->subnqn, NVME_DISC_SUBSYS_NAME"nqn.2014-08.org.nvmexpress.discovery"));
1279 break;
1280 case NVME_NQN_DISC:
1281 if (discover)
1282 *discover = true1;
1283 libnvme_ctrl_set_discovery_ctrl(c, true1);
1284 libnvme_ctrl_set_unique_discovery_ctrl(c,
1285 strcmp(e->subnqn, NVME_DISC_SUBSYS_NAME"nqn.2014-08.org.nvmexpress.discovery"));
1286 break;
1287 default:
1288 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)
1289 e->subtype)__libnvme_msg(h->ctx, LIBNVME_LOG_ERR, ((void*)0), "unsupported subtype %d\n"
, e->subtype)
;
1290 fallthrough__attribute__((fallthrough));
1291 case NVME_NQN_NVME:
1292 libnvme_ctrl_set_discovery_ctrl(c, false0);
1293 libnvme_ctrl_set_unique_discovery_ctrl(c, false0);
1294 break;
1295 }
1296
1297 if (libnvme_ctrl_get_discovered(c)) {
1298 libnvme_free_ctrl(c);
1299 return -EAGAIN11;
1300 }
1301
1302 if (e->treq & NVMF_TREQ_DISABLE_SQFLOW &&
1303 nvmf_check_option(h->ctx, disable_sqflow)({ !__nvmf_supported_options(h->ctx) && h->ctx->
options->disable_sqflow; })
)
1304 c->cfg.disable_sqflow = true1;
1305
1306 /* update tls or concat */
1307 nvmf_update_tls_concat(e, c, h);
1308
1309 ret = libnvmf_add_ctrl(h, c);
1310 if (!ret) {
1311 *cp = c;
1312 return 0;
1313 }
1314
1315 if (ret == EINVAL22 && c->cfg.disable_sqflow) {
1316 /* disable_sqflow is unrecognized option on older kernels */
1317 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")
1318 "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")
;
1319 c->cfg.disable_sqflow = false0;
1320 ret = libnvmf_add_ctrl(h, c);
1321 if (!ret) {
1322 *cp = c;
1323 return 0;
1324 }
1325 }
1326 libnvme_free_ctrl(c);
1327 return -ENOENT2;
1328}
1329
1330/*
1331 * Most of nvmf_discovery_log is reserved, so only fetch the initial bytes.
1332 * 8 bytes for GENCTR, 8 for NUMREC, and 2 for RECFMT.
1333 * Since only multiples of 4 bytes are allowed, round 18 up to 20.
1334 */
1335#define DISCOVERY_HEADER_LEN20 20
1336
1337static int nvme_discovery_log(libnvme_ctrl_t ctrl,
1338 const struct libnvmf_discovery_args *args,
1339 struct nvmf_discovery_log **logp)
1340{
1341 struct libnvme_global_ctx *ctx = ctrl->ctx;
1342 struct nvmf_discovery_log *log;
1343 int retries = 0;
1344 int err;
1345 const char *name = libnvme_ctrl_get_name(ctrl);
1346 uint64_t genctr, numrec;
1347 struct libnvme_transport_handle *hdl;
1348
1349 hdl = libnvme_ctrl_get_transport_handle(ctrl);
1350 struct libnvme_passthru_cmd cmd;
1351
1352 log = libnvme_alloc(sizeof(*log));
1353 if (!log) {
1354 libnvme_msg(ctx, LIBNVME_LOG_ERR,__libnvme_msg(ctx, LIBNVME_LOG_ERR, ((void*)0), "could not allocate memory for discovery log header\n"
)
1355 "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"
)
;
1356 return -ENOMEM12;
1357 }
1358
1359 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)
1360 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)
;
1361 nvme_init_get_log_discovery(&cmd, 0, log, DISCOVERY_HEADER_LEN20);
1362 err = libnvme_get_log(hdl, &cmd, false0, DISCOVERY_HEADER_LEN20);
1363 if (err) {
1364 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)
1365 "%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)
1366 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)
;
1367 goto out_free_log;
1368 }
1369
1370 do {
1371 size_t entries_size;
1372
1373 numrec = le64_to_cpu(log->numrec);
1374 genctr = le64_to_cpu(log->genctr);
1375
1376 if (numrec == 0)
1377 break;
1378
1379 libnvme_free(log);
1380 entries_size = sizeof(*log->entries) * numrec;
1381 log = libnvme_alloc(sizeof(*log) + entries_size);
1382 if (!log) {
1383 libnvme_msg(ctx, LIBNVME_LOG_ERR,__libnvme_msg(ctx, LIBNVME_LOG_ERR, ((void*)0), "could not alloc memory for discovery log page\n"
)
1384 "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"
)
;
1385 return -ENOMEM12;
1386 }
1387
1388 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
)
1389 "%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
)
1390 name, numrec, genctr)__libnvme_msg(ctx, LIBNVME_LOG_DEBUG, ((void*)0), "%s: get %"
"l" "u" " records (genctr %" "l" "u" ")\n", name, numrec, genctr
)
;
1391
1392 nvme_init_get_log_discovery(&cmd, sizeof(*log), log->entries, entries_size);
1393 cmd.cdw10 |= NVME_FIELD_ENCODE(args->lsp,(((__u32)(args->lsp) & (NVME_LOG_CDW10_LSP_MASK)) <<
(NVME_LOG_CDW10_LSP_SHIFT))
1394 NVME_LOG_CDW10_LSP_SHIFT,(((__u32)(args->lsp) & (NVME_LOG_CDW10_LSP_MASK)) <<
(NVME_LOG_CDW10_LSP_SHIFT))
1395 NVME_LOG_CDW10_LSP_MASK)(((__u32)(args->lsp) & (NVME_LOG_CDW10_LSP_MASK)) <<
(NVME_LOG_CDW10_LSP_SHIFT))
;
1396 err = libnvme_get_log(hdl, &cmd, false0, NVME_LOG_PAGE_PDU_SIZE4096);
1397 if (err) {
1398 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)
1399 "%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)
1400 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)
;
1401 goto out_free_log;
1402 }
1403
1404 /*
1405 * If the log page was read with multiple Get Log Page commands,
1406 * genctr must be checked afterwards to ensure atomicity
1407 */
1408 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)
;
1409
1410 nvme_init_get_log_discovery(&cmd, 0, log, DISCOVERY_HEADER_LEN20);
1411 err = libnvme_get_log(hdl, &cmd, false0, DISCOVERY_HEADER_LEN20);
1412 if (err) {
1413 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)
1414 "%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)
1415 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)
;
1416 goto out_free_log;
1417 }
1418 } while (genctr != le64_to_cpu(log->genctr) &&
1419 ++retries < args->max_retries);
1420
1421 if (genctr != le64_to_cpu(log->genctr)) {
1422 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)
;
1423 err = -EAGAIN11;
1424 } else if (numrec != le64_to_cpu(log->numrec)) {
1425 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))
1426 "%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))
1427 "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))
1428 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))
;
1429 err = -EBADSLT57;
1430 } else {
1431 *logp = log;
1432 return 0;
1433 }
1434
1435out_free_log:
1436 libnvme_free(log);
1437 return err;
1438}
1439
1440static void sanitize_discovery_log_entry(struct libnvme_global_ctx *ctx,
1441 struct nvmf_disc_log_entry *e)
1442{
1443 strchomp(e->trsvcid, sizeof(e->trsvcid));
1444 strchomp(e->traddr, sizeof(e->traddr));
1445
1446 /*
1447 * Report traddr always in 'nn-0x:pn-0x' format, but some discovery logs
1448 * provide 'nn-0x,pn-0x'.
1449 */
1450 if (e->trtype == NVMF_TRTYPE_FC) {
1451 char *comma = strchr(e->traddr, ',');
1452
1453 if (comma) {
1454 libnvme_msg(ctx, LIBNVME_LOG_WARN,__libnvme_msg(ctx, LIBNVME_LOG_WARN, ((void*)0), "invalid traddr separator ',' instead ':', fixing it"
)
1455 "invalid traddr separator ',' instead ':', fixing it")__libnvme_msg(ctx, LIBNVME_LOG_WARN, ((void*)0), "invalid traddr separator ',' instead ':', fixing it"
)
;
1456 *comma = ':';
1457 }
1458 }
1459}
1460
1461__libnvme_public__attribute__((visibility("default"))) int libnvmf_get_discovery_log(libnvme_ctrl_t ctrl,
1462 const struct libnvmf_discovery_args *args,
1463 struct nvmf_discovery_log **logp)
1464{
1465 static const struct libnvmf_discovery_args defaults = {
1466 .max_retries = 6,
1467 .lsp = NVMF_LOG_DISC_LSP_NONE,
1468 };
1469 struct nvmf_discovery_log *log;
1470 int err;
1471
1472 if (!args)
1473 args = &defaults;
1474
1475 err = nvme_discovery_log(ctrl, args, &log);
1476 if (err)
1477 return err;
1478
1479 for (int i = 0; i < le64_to_cpu(log->numrec); i++)
1480 sanitize_discovery_log_entry(ctrl->ctx, &log->entries[i]);
1481
1482 *logp = log;
1483 return 0;
1484}
1485
1486
1487/**
1488 * nvmf_get_tel() - Calculate the amount of memory needed for a DIE.
1489 * @hostsymname: Symbolic name (may be NULL)
1490 *
1491 * Each Discovery Information Entry (DIE) must contain at a minimum an
1492 * Extended Attribute for the HostID. The Entry may optionally contain an
1493 * Extended Attribute for the Symbolic Name.
1494 *
1495 * Return: Total Entry Length
1496 */
1497static __u32 nvmf_get_tel(const char *hostsymname)
1498{
1499 __u32 tel = sizeof(struct nvmf_ext_die);
1500 __u16 len;
1501
1502 /* Host ID is mandatory */
1503 tel += libnvmf_exat_size(NVME_UUID_LEN16);
1504
1505 /* Symbolic name is optional */
1506 len = hostsymname ? strlen(hostsymname) : 0;
1507 if (len)
1508 tel += libnvmf_exat_size(len);
1509
1510 return tel;
1511}
1512
1513/**
1514 * nvmf_fill_die() - Fill a Discovery Information Entry.
1515 * @die: Pointer to Discovery Information Entry to be filled
1516 * @h: Pointer to the host data structure
1517 * @tel: Length of the DIE
1518 * @trtype: Transport type
1519 * @adrfam: Address family
1520 * @reg_addr: Address to register. Setting this to an empty string tells
1521 * the DC to infer address from the source address of the socket.
1522 * @tsas: Transport Specific Address Subtype for the address being
1523 * registered.
1524 */
1525static void nvmf_fill_die(struct nvmf_ext_die *die, struct libnvme_host *h,
1526 __u32 tel, __u8 trtype, __u8 adrfam,
1527 const char *reg_addr, union nvmf_tsas *tsas)
1528{
1529 __u16 numexat = 0;
1530 size_t symname_len;
1531 struct nvmf_ext_attr *exat;
1532
1533 die->tel = cpu_to_le32(tel);
1534 die->trtype = trtype;
1535 die->adrfam = adrfam;
1536
1537 memcpy(die->nqn, h->hostnqn, MIN(sizeof(die->nqn), strlen(h->hostnqn))(((sizeof(die->nqn))<(strlen(h->hostnqn)))?(sizeof(die
->nqn)):(strlen(h->hostnqn)))
);
1538 memcpy(die->traddr, reg_addr, MIN(sizeof(die->traddr), strlen(reg_addr))(((sizeof(die->traddr))<(strlen(reg_addr)))?(sizeof(die
->traddr)):(strlen(reg_addr)))
);
1539
1540 if (tsas)
1541 memcpy(&die->tsas, tsas, sizeof(die->tsas));
1542
1543 /* Extended Attribute for the HostID (mandatory) */
1544 numexat++;
1545 exat = die->exat;
1546 exat->exattype = cpu_to_le16(NVMF_EXATTYPE_HOSTID);
1547 exat->exatlen = cpu_to_le16(libnvmf_exat_len(NVME_UUID_LEN16));
1548 libnvme_uuid_from_string(h->hostid, exat->exatval);
1549
1550 /* Extended Attribute for the Symbolic Name (optional) */
1551 symname_len = h->hostsymname ? strlen(h->hostsymname) : 0;
1552 if (symname_len) {
1553 __u16 exatlen = libnvmf_exat_len(symname_len);
1554
1555 numexat++;
1556 exat = libnvmf_exat_ptr_next(exat);
1557 exat->exattype = cpu_to_le16(NVMF_EXATTYPE_SYMNAME);
1558 exat->exatlen = cpu_to_le16(exatlen);
1559 memcpy(exat->exatval, h->hostsymname, symname_len);
1560 /* Per Base specs, ASCII strings must be padded with spaces */
1561 memset(&exat->exatval[symname_len], ' ', exatlen - symname_len);
1562 }
1563
1564 die->numexat = cpu_to_le16(numexat);
1565}
1566
1567/**
1568 * nvmf_dim() - Explicit reg, dereg, reg-update issuing DIM
1569 * @c: Host NVMe controller instance maintaining the admin queue used to
1570 * submit the DIM command to the DC.
1571 * @tas: Task field of the Command Dword 10 (cdw10). Indicates whether to
1572 * perform a Registration, Deregistration, or Registration-update.
1573 * @trtype: Transport type (&enum nvmf_trtype - must be NVMF_TRTYPE_TCP)
1574 * @adrfam: Address family (&enum nvmf_addr_family)
1575 * @reg_addr: Address to register. Setting this to an empty string tells
1576 * the DC to infer address from the source address of the socket.
1577 * @tsas: Transport Specific Address Subtype for the address being
1578 * registered.
1579 * @result: Location where to save the command-specific result returned by
1580 * the discovery controller.
1581 *
1582 * Perform explicit registration, deregistration, or
1583 * registration-update (specified by @tas) by sending a Discovery
1584 * Information Management (DIM) command to the Discovery Controller
1585 * (DC).
1586 *
1587 * Return: 0 on success; on failure -1 is returned and errno is set
1588 */
1589static int nvmf_dim(libnvme_ctrl_t c, enum nvmf_dim_tas tas, __u8 trtype,
1590 __u8 adrfam, const char *reg_addr, union nvmf_tsas *tsas,
1591 __u32 *result)
1592{
1593 struct libnvme_global_ctx *ctx = c->s && c->s->h ? c->s->h->ctx : NULL((void*)0);
1594 __cleanup_free__attribute__((cleanup(freep))) struct nvmf_dim_data *dim = NULL((void*)0);
1595 struct libnvme_transport_handle *hdl = libnvme_ctrl_get_transport_handle(c);
1596 struct libnvme_passthru_cmd cmd;
1597 struct nvmf_ext_die *die;
1598 __u32 tdl;
1599 __u32 tel;
1600 int ret;
1601
1602 if (!c->s) {
1603 libnvme_msg(ctx, LIBNVME_LOG_ERR,__libnvme_msg(ctx, LIBNVME_LOG_ERR, ((void*)0), "%s: failed to perform DIM. subsystem undefined.\n"
, c->name)
1604 "%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)
1605 c->name)__libnvme_msg(ctx, LIBNVME_LOG_ERR, ((void*)0), "%s: failed to perform DIM. subsystem undefined.\n"
, c->name)
;
1606 return -EINVAL22;
1607 }
1608
1609 if (!c->s->h) {
1610 libnvme_msg(ctx, LIBNVME_LOG_ERR,__libnvme_msg(ctx, LIBNVME_LOG_ERR, ((void*)0), "%s: failed to perform DIM. host undefined.\n"
, c->name)
1611 "%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)
1612 c->name)__libnvme_msg(ctx, LIBNVME_LOG_ERR, ((void*)0), "%s: failed to perform DIM. host undefined.\n"
, c->name)
;
1613 return -EINVAL22;
1614 }
1615
1616 if (!c->s->h->hostid) {
1617 libnvme_msg(ctx, LIBNVME_LOG_ERR,__libnvme_msg(ctx, LIBNVME_LOG_ERR, ((void*)0), "%s: failed to perform DIM. hostid undefined.\n"
, c->name)
1618 "%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)
1619 c->name)__libnvme_msg(ctx, LIBNVME_LOG_ERR, ((void*)0), "%s: failed to perform DIM. hostid undefined.\n"
, c->name)
;
1620 return -EINVAL22;
1621 }
1622
1623 if (!c->s->h->hostnqn) {
1624 libnvme_msg(ctx, LIBNVME_LOG_ERR,__libnvme_msg(ctx, LIBNVME_LOG_ERR, ((void*)0), "%s: failed to perform DIM. hostnqn undefined.\n"
, c->name)
1625 "%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)
1626 c->name)__libnvme_msg(ctx, LIBNVME_LOG_ERR, ((void*)0), "%s: failed to perform DIM. hostnqn undefined.\n"
, c->name)
;
1627 return -EINVAL22;
1628 }
1629
1630 if (strcmp(c->transport, "tcp")) {
1631 libnvme_msg(ctx, LIBNVME_LOG_ERR,__libnvme_msg(ctx, LIBNVME_LOG_ERR, ((void*)0), "%s: DIM only supported for TCP connections.\n"
, c->name)
1632 "%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)
1633 c->name)__libnvme_msg(ctx, LIBNVME_LOG_ERR, ((void*)0), "%s: DIM only supported for TCP connections.\n"
, c->name)
;
1634 return -EINVAL22;
1635 }
1636
1637 /* Register one Discovery Information Entry (DIE) of size TEL */
1638 tel = nvmf_get_tel(c->s->h->hostsymname);
1639 tdl = sizeof(struct nvmf_dim_data) + tel;
1640
1641 dim = (struct nvmf_dim_data *)calloc(1, tdl);
1642 if (!dim) {
1643 return -ENOMEM12;
1644 }
1645
1646 dim->tdl = cpu_to_le32(tdl);
1647 dim->nument = cpu_to_le64(1); /* only one DIE to register */
1648 dim->entfmt = cpu_to_le16(NVMF_DIM_ENTFMT_EXTENDED);
1649 dim->etype = cpu_to_le16(NVMF_DIM_ETYPE_HOST);
1650 dim->ektype = cpu_to_le16(0x5F); /* must be 0x5F per specs */
1651
1652 memcpy(dim->eid, c->s->h->hostnqn,
1653 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)))
);
1654
1655 ret = libnvmf_get_entity_name(dim->ename, sizeof(dim->ename));
1656 if (ret < 0)
1657 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))
1658 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))
;
1659 else if (ret == 0)
1660 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)
1661 c->name)__libnvme_msg(ctx, LIBNVME_LOG_INFO, ((void*)0), "%s: Failed to retrieve ENAME.\n"
, c->name)
;
1662
1663 ret = libnvmf_get_entity_version(dim->ever, sizeof(dim->ever));
1664 if (ret <= 0)
1665 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)
;
1666
1667 die = &dim->die->extended;
1668 nvmf_fill_die(die, c->s->h, tel, trtype, adrfam, reg_addr, tsas);
1669
1670 nvme_init_dim_send(&cmd, tas, dim, tdl);
1671 return libnvme_exec_admin_passthru(hdl, &cmd);
1672}
1673
1674/**
1675 * nvme_get_adrfam() - Get address family for the address we're registering
1676 * with the DC.
1677 *
1678 * We retrieve this info from the socket itself. If we can't get the source
1679 * address from the socket, then we'll infer the address family from the
1680 * address of the DC since the DC address has the same address family.
1681 *
1682 * @c: Host NVMe controller instance maintaining the admin queue used to
1683 * submit the DIM command to the DC.
1684 *
1685 * Return: The address family of the source address associated with the
1686 * socket connected to the DC.
1687 */
1688static __u8 nvme_get_adrfam(libnvme_ctrl_t c)
1689{
1690 struct sockaddr_storage addr;
1691 __u8 adrfam = NVMF_ADDR_FAMILY_IP4;
1692 struct libnvme_global_ctx *ctx = c->s && c->s->h ? c->s->h->ctx : NULL((void*)0);
1693
1694 if (!inet_pton_with_scope(ctx, AF_UNSPEC0, c->traddr, c->trsvcid, &addr)) {
1695 if (addr.ss_family == AF_INET610)
1696 adrfam = NVMF_ADDR_FAMILY_IP6;
1697 }
1698
1699 return adrfam;
1700}
1701
1702/* These string definitions must match with the kernel */
1703static const char *cntrltype_str[] = {
1704 [NVME_CTRL_CNTRLTYPE_IO] = "io",
1705 [NVME_CTRL_CNTRLTYPE_DISCOVERY] = "discovery",
1706 [NVME_CTRL_CNTRLTYPE_ADMIN] = "admin",
1707};
1708
1709static const char *dctype_str[] = {
1710 [NVME_CTRL_DCTYPE_NOT_REPORTED] = "none",
1711 [NVME_CTRL_DCTYPE_DDC] = "ddc",
1712 [NVME_CTRL_DCTYPE_CDC] = "cdc",
1713};
1714
1715/**
1716 * nvme_fetch_cntrltype_dctype_from_id - Get cntrltype and dctype from identify command
1717 * @c: Controller instance
1718 *
1719 * On legacy kernels the cntrltype and dctype are not exposed through the
1720 * sysfs. We must get them directly from the controller by performing an
1721 * identify command.
1722 */
1723static int nvme_fetch_cntrltype_dctype_from_id(libnvme_ctrl_t c)
1724{
1725 __cleanup_libnvme_free__attribute__((cleanup(libnvme_freep))) struct nvme_id_ctrl *id = NULL((void*)0);
1726 int ret;
1727
1728 id = libnvme_alloc(sizeof(*id));
1729 if (!id)
1730 return -ENOMEM12;
1731
1732 ret = libnvme_ctrl_identify(c, id);
1733 if (ret)
1734 return ret;
1735
1736 if (!c->cntrltype) {
1737 if (id->cntrltype > NVME_CTRL_CNTRLTYPE_ADMIN || !cntrltype_str[id->cntrltype])
1738 c->cntrltype = strdup("reserved");
1739 else
1740 c->cntrltype = strdup(cntrltype_str[id->cntrltype]);
1741 }
1742
1743 if (!c->dctype) {
1744 if (id->dctype > NVME_CTRL_DCTYPE_CDC || !dctype_str[id->dctype])
1745 c->dctype = strdup("reserved");
1746 else
1747 c->dctype = strdup(dctype_str[id->dctype]);
1748 }
1749 return 0;
1750}
1751
1752__libnvme_public__attribute__((visibility("default"))) bool_Bool libnvmf_is_registration_supported(libnvme_ctrl_t c)
1753{
1754 if (!c->cntrltype || !c->dctype)
1755 if (nvme_fetch_cntrltype_dctype_from_id(c))
1756 return false0;
1757
1758 return !strcmp(c->dctype, "ddc") || !strcmp(c->dctype, "cdc");
1759}
1760
1761__libnvme_public__attribute__((visibility("default"))) int libnvmf_register_ctrl(
1762 libnvme_ctrl_t c, enum nvmf_dim_tas tas, __u32 *result)
1763{
1764 if (!libnvmf_is_registration_supported(c))
1765 return -ENOTSUP95;
1766
1767 /* We're registering our source address with the DC. To do
1768 * that, we can simply send an empty string. This tells the DC
1769 * to retrieve the source address from the socket and use that
1770 * as the registration address.
1771 */
1772 return nvmf_dim(c, tas, NVMF_TRTYPE_TCP, nvme_get_adrfam(c), "", NULL((void*)0), result);
1773}
1774
1775#define IS_XDIGIT(c)((c >= '0' && c <= '9') || (c >= 'A' &&
c <= 'F') || (c >= 'a' && c <= 'f'))
((c >= '0' && c <= '9') || \
1776 (c >= 'A' && c <= 'F') || \
1777 (c >= 'a' && c <= 'f'))
1778#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' : ( \
1779 (c >= 'A' && c <= 'F') ? c - 'A' + 10 : c - 'a' + 10))
1780
1781/* returns newly allocated string */
1782static char *unescape_uri(const char *str, int len)
1783{
1784 char *dst;
1785 int l;
1786 int i, j;
1787
1788 l = len > 0 ? len : strlen(str);
1789 dst = malloc(l + 1);
1790 for (i = 0, j = 0; i < l; i++, j++) {
1791 if (str[i] == '%' && i + 2 < l &&
1792 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'))
) {
1793 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) +
1794 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))
;
1795 i += 2;
1796 } else
1797 dst[j] = str[i];
1798 }
1799 dst[j] = '\0';
1800 return dst;
1801}
1802
1803__libnvme_public__attribute__((visibility("default"))) int libnvmf_uri_parse(
1804 const char *str, struct libnvmf_uri **urip)
1805{
1806 __cleanup_uri__attribute__((cleanup(free_uri))) struct libnvmf_uri *uri = NULL((void*)0);
1807 __cleanup_free__attribute__((cleanup(freep))) char *scheme = NULL((void*)0);
1808 __cleanup_free__attribute__((cleanup(freep))) char *authority = NULL((void*)0);
1809 __cleanup_free__attribute__((cleanup(freep))) char *path = NULL((void*)0);
1810 __cleanup_free__attribute__((cleanup(freep))) char *h = NULL((void*)0);
1811 const char *host;
1812 int i;
1813
1814 /* As defined in Boot Specification rev. 1.0:
1815 *
1816 * section 1.5.7: NVMe-oF URI Format
1817 * nvme+tcp://192.168.1.1:4420/
1818 * nvme+tcp://[FE80::1010]:4420/
1819 *
1820 * section 3.1.2.5.3: DHCP Root-Path - a hierarchical NVMe-oF URI Format
1821 * NVME<+PROTOCOL>://<SERVERNAME/IP>[:TRANSPORT PORT]/<SUBSYS NQN>/<NID>
1822 * or
1823 * NVME<+PROTOCOL>://<DISCOVERY CONTROLLER ADDRESS>[:DISCOVERY-
1824 * -CONTROLLER PORT]/NQN.2014-08.ORG.NVMEXPRESS.DISCOVERY/<NID>
1825 */
1826
1827 uri = calloc(1, sizeof(struct libnvmf_uri));
1828 if (!uri)
1829 return -ENOMEM12;
1830
1831 if (sscanf(str, "%m[^:/]://%m[^/?#]%ms",
1832 &scheme, &authority, &path) < 2)
1833 return -EINVAL22;
1834
1835 if (sscanf(scheme, "%m[^+]+%ms",
1836 &uri->scheme, &uri->protocol) < 1)
1837 return -EINVAL22;
1838
1839 /* split userinfo */
1840 host = strrchr(authority, '@');
1841 if (host) {
1842 host++;
1843 uri->userinfo = unescape_uri(authority, host - authority);
1844 } else
1845 host = authority;
1846
1847 /* try matching IPv6 address first */
1848 if (sscanf(host, "[%m[^]]]:%d",
1849 &uri->host, &uri->port) < 1) {
1850 /* treat it as IPv4/hostname */
1851 if (sscanf(host, "%m[^:]:%d",
1852 &h, &uri->port) < 1)
1853 return -EINVAL22;
1854 uri->host = unescape_uri(h, 0);
1855 }
1856
1857 /* split path into elements */
1858 if (path) {
1859 char *e, *elem;
1860
1861 /* separate the fragment */
1862 e = strrchr(path, '#');
1863 if (e) {
1864 uri->fragment = unescape_uri(e + 1, 0);
1865 *e = '\0';
1866 }
1867 /* separate the query string */
1868 e = strrchr(path, '?');
1869 if (e) {
1870 uri->query = unescape_uri(e + 1, 0);
1871 *e = '\0';
1872 }
1873
1874 /* count elements first */
1875 for (i = 0, e = path; *e; e++)
1876 if (*e == '/' && *(e + 1) != '/')
1877 i++;
1878 uri->path_segments = calloc(i + 2, sizeof(char *));
1879
1880 i = 0;
1881 elem = strtok_r(path, "/", &e);
1882 if (elem)
1883 uri->path_segments[i++] = unescape_uri(elem, 0);
1884 while (elem && strlen(elem)) {
1885 elem = strtok_r(NULL((void*)0), "/", &e);
1886 if (elem)
1887 uri->path_segments[i++] = unescape_uri(elem, 0);
1888 }
1889 }
1890
1891 *urip = uri;
1892 uri = NULL((void*)0);
1893
1894 return 0;
1895}
1896
1897__libnvme_public__attribute__((visibility("default"))) void libnvmf_uri_free(struct libnvmf_uri *uri)
1898{
1899 char **s;
1900
1901 if (!uri)
1902 return;
1903 free(uri->scheme);
1904 free(uri->protocol);
1905 free(uri->userinfo);
1906 free(uri->host);
1907 for (s = uri->path_segments; s && *s; s++)
1908 free(*s);
1909 free(uri->path_segments);
1910 free(uri->query);
1911 free(uri->fragment);
1912 free(uri);
1913}
1914
1915static libnvme_ctrl_t lookup_ctrl(libnvme_host_t h, struct libnvmf_context *fctx)
1916{
1917 libnvme_subsystem_t s;
1918 libnvme_ctrl_t c;
1919
1920 libnvme_for_each_subsystem(h, s)for (s = libnvme_first_subsystem(h); s != ((void*)0); s = libnvme_next_subsystem
(h, s))
{
1921 c = libnvmf_ctrl_find(s, fctx);
1922 if (c)
1923 return c;
1924 }
1925
1926 return NULL((void*)0);
1927}
1928
1929static int lookup_host(struct libnvme_global_ctx *ctx,
1930 struct libnvmf_context *fctx, struct libnvme_host **host)
1931{
1932 __cleanup_free__attribute__((cleanup(freep))) char *hnqn = NULL((void*)0);
1933 __cleanup_free__attribute__((cleanup(freep))) char *hid = NULL((void*)0);
1934 struct libnvme_host *h;
1935 int err;
1936
1937 err = libnvme_host_get_ids(ctx, fctx->hostnqn, fctx->hostid, &hnqn, &hid);
1938 if (err < 0)
1939 return err;
1940
1941 h = libnvme_lookup_host(ctx, hnqn, hid);
1942 if (!h)
1943 return -ENOMEM12;
1944
1945 *host = h;
1946
1947 return 0;
1948}
1949
1950static int setup_connection(struct libnvmf_context *fctx, struct libnvme_host *h,
1951 bool_Bool discovery)
1952{
1953 if (fctx->hostkey)
1954 libnvme_host_set_dhchap_host_key(h, fctx->hostkey);
1955
1956 if (!fctx->ctrl_params.trsvcid)
1957 fctx->ctrl_params.trsvcid =
1958 libnvmf_get_default_trsvcid(fctx->ctrl_params.transport,
1959 discovery);
1960
1961 return 0;
1962}
1963
1964
1965static int set_discovery_kato(struct libnvmf_context *fctx)
1966{
1967 int tmo = fctx->ctrl_params.cfg.keep_alive_tmo;
1968
1969 /* Set kato to NVMF_DEF_DISC_TMO for persistent controllers */
1970 if (fctx->persistent && !fctx->ctrl_params.cfg.keep_alive_tmo)
1971 fctx->ctrl_params.cfg.keep_alive_tmo =
1972 fctx->default_keep_alive_timeout;
1973 /* Set kato to zero for non-persistent controllers */
1974 else if (!fctx->persistent &&
1975 (fctx->ctrl_params.cfg.keep_alive_tmo > 0))
1976 fctx->ctrl_params.cfg.keep_alive_tmo = 0;
1977
1978 return tmo;
1979}
1980
1981static void nvme_parse_tls_args(const char *keyring, const char *tls_key,
1982 const char *tls_key_identity,
1983 struct libnvme_fabrics_config *cfg, libnvme_ctrl_t c)
1984{
1985 if (keyring) {
1986 char *endptr;
1987 long id = strtol(keyring, &endptr, 0);
1988
1989 if (endptr != keyring)
1990 cfg->keyring_id = id;
1991 else
1992 libnvme_ctrl_set_keyring(c, keyring);
1993 }
1994
1995 if (tls_key_identity)
1996 libnvme_ctrl_set_tls_key_identity(c, tls_key_identity);
1997
1998 if (tls_key) {
1999 char *endptr;
2000 long id = strtol(tls_key, &endptr, 0);
2001
2002 if (endptr != tls_key)
2003 cfg->tls_key_id = id;
2004 else
2005 libnvme_ctrl_set_tls_key(c, tls_key);
2006 }
2007}
2008
2009
2010static int _nvmf_discovery(struct libnvme_global_ctx *ctx,
2011 struct libnvmf_context *fctx, bool_Bool connect,
2012 struct libnvme_ctrl *c)
2013{
2014 __cleanup_free__attribute__((cleanup(freep))) struct nvmf_discovery_log *log = NULL((void*)0);
2015 libnvme_subsystem_t s = libnvme_ctrl_get_subsystem(c);
2016 libnvme_host_t h = libnvme_subsystem_get_host(s);
2017 uint64_t numrec;
2018 int err;
2019
2020 struct libnvmf_discovery_args args = {
2021 .max_retries = fctx->default_max_discovery_retries,
2022 .lsp = NVMF_LOG_DISC_LSP_NONE,
2023 };
2024
2025 err = nvme_discovery_log(c, &args, &log);
2026 if (err) {
2027 libnvme_msg(ctx, LIBNVME_LOG_ERR, "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))
2028 libnvme_strerror(err))__libnvme_msg(ctx, LIBNVME_LOG_ERR, ((void*)0), "failed to get discovery log: %s\n"
, libnvme_strerror(err))
;
2029 return err;
2030 }
2031
2032 numrec = le64_to_cpu(log->numrec);
2033 if (fctx->hooks.discovery_log)
2034 fctx->hooks.discovery_log(fctx, connect, log, numrec,
2035 fctx->hooks.user_data);
2036
2037 if (!connect)
2038 return 0;
2039
2040 for (int i = 0; i < numrec; i++) {
2041 struct nvmf_disc_log_entry *e = &log->entries[i];
2042 libnvme_ctrl_t cl;
2043 bool_Bool discover = false0;
2044 bool_Bool disconnect;
2045 libnvme_ctrl_t child = { 0 };
2046 int tmo = fctx->ctrl_params.cfg.keep_alive_tmo;
2047 struct libnvmf_context nfctx = *fctx;
2048
2049 sanitize_discovery_log_entry(c->ctx, e);
2050
2051 nfctx.ctrl_params.subsysnqn = e->subnqn;
2052 nfctx.ctrl_params.transport = libnvmf_trtype_str(e->trtype);
2053 nfctx.ctrl_params.traddr = e->traddr;
2054 nfctx.ctrl_params.trsvcid = e->trsvcid;
2055
2056 /* Already connected ? */
2057 cl = lookup_ctrl(h, &nfctx);
2058 if (cl && libnvme_ctrl_get_name(cl))
2059 continue;
2060
2061 /* Skip connect if the transport types don't match */
2062 if (strcmp(libnvme_ctrl_get_transport(c),
2063 nfctx.ctrl_params.transport))
2064 continue;
2065
2066 if (e->subtype == NVME_NQN_DISC ||
2067 e->subtype == NVME_NQN_CURR) {
2068 __u16 eflags = le16_to_cpu(e->eflags);
2069 /*
2070 * Does this discovery controller return the
2071 * same information?
2072 */
2073 if (eflags & NVMF_DISC_EFLAGS_DUPRETINFO)
2074 continue;
2075
2076 /*
2077 * Are we supposed to keep the discovery
2078 * controller around?
2079 */
2080 disconnect = !nfctx.persistent;
2081
2082 if (strcmp(e->subnqn, NVME_DISC_SUBSYS_NAME"nqn.2014-08.org.nvmexpress.discovery")) {
2083 /*
2084 * Does this discovery controller doesn't
2085 * support explicit persistent connection?
2086 */
2087 if (!(eflags & NVMF_DISC_EFLAGS_EPCSD))
2088 disconnect = true1;
2089 else
2090 disconnect = false0;
2091 }
2092
2093 set_discovery_kato(&nfctx);
2094 } else {
2095 /* NVME_NQN_NVME */
2096 disconnect = false0;
2097 }
2098
2099 err = nvmf_connect_disc_entry(h, e, &nfctx, &discover, &child);
2100
2101 nfctx.ctrl_params.cfg.keep_alive_tmo = tmo;
2102
2103 if (!child) {
2104 if (discover)
2105 _nvmf_discovery(ctx, &nfctx, true1, child);
2106
2107 if (child && disconnect) {
2108 libnvmf_disconnect_ctrl(child);
2109 libnvme_free_ctrl(child);
2110 }
2111 } else if (err == -ENVME_CONNECT_ALREADY) {
2112 struct nvmf_disc_log_entry *e = &log->entries[i];
2113
2114 nfctx.hooks.already_connected(&nfctx, h, e->subnqn,
2115 libnvmf_trtype_str(e->trtype), e->traddr,
2116 e->trsvcid, nfctx.hooks.user_data);
2117 }
2118 }
2119
2120 return 0;
2121}
2122
2123__libnvme_public__attribute__((visibility("default"))) const char *libnvmf_get_default_trsvcid(const char *transport,
2124 bool_Bool discovery_ctrl)
2125{
2126 if (!transport)
2127 return NULL((void*)0);
2128 if (!strcmp(transport, "tcp")) {
2129 if (discovery_ctrl)
2130 /* Default port for NVMe/TCP discovery controllers */
2131 return stringify(NVME_DISC_IP_PORT)"8009";
2132 /* Default port for NVMe/TCP io controllers */
2133 return stringify(NVME_RDMA_IP_PORT)"4420";
2134 } else if (!strcmp(transport, "rdma")) {
2135 /* Default port for NVMe/RDMA controllers */
2136 return stringify(NVME_RDMA_IP_PORT)"4420";
2137 }
2138
2139 return NULL((void*)0);
2140}
2141
2142static bool_Bool is_persistent_discovery_ctrl(libnvme_host_t h, libnvme_ctrl_t c)
2143{
2144 if (libnvme_host_is_pdc_enabled(h, DEFAULT_PDC_ENABLED0))
2145 return libnvme_ctrl_get_unique_discovery_ctrl(c);
2146
2147 return false0;
2148}
2149
2150static int libnvme_add_ctrl(struct libnvmf_context *fctx,
2151 struct libnvme_host *h, struct libnvme_ctrl *c)
2152{
2153 int err;
2154
2155retry:
2156 err = libnvmf_add_ctrl(h, c);
2157 if (!err)
2158 return 0;
2159 if (fctx->hooks.decide_retry(fctx, err, fctx->hooks.user_data))
2160 goto retry;
2161
2162 return err;
2163}
2164
2165static int __create_discovery_ctrl(struct libnvme_global_ctx *ctx,
2166 struct libnvmf_context *fctx, libnvme_host_t h,
2167 struct libnvme_ctrl **ctrl)
2168{
2169 libnvme_ctrl_t c;
2170 int tmo, ret;
2171
2172 ret = libnvme_create_ctrl(ctx, &fctx->ctrl_params, &c);
2173 if (ret)
2174 return ret;
2175
2176 libnvme_ctrl_set_discovery_ctrl(c, true1);
2177 libnvme_ctrl_set_unique_discovery_ctrl(c,
2178 strcmp(fctx->ctrl_params.subsysnqn,
2179 NVME_DISC_SUBSYS_NAME"nqn.2014-08.org.nvmexpress.discovery"));
2180 tmo = set_discovery_kato(fctx);
2181
2182 if (libnvme_ctrl_get_unique_discovery_ctrl(c) && fctx->hostkey) {
2183 libnvme_ctrl_set_dhchap_host_key(c, fctx->hostkey);
2184 if (fctx->ctrlkey)
2185 libnvme_ctrl_set_dhchap_ctrl_key(c, fctx->ctrlkey);
2186 }
2187
2188 ret = libnvme_add_ctrl(fctx, h, c);
2189 fctx->ctrl_params.cfg.keep_alive_tmo = tmo;
2190 if (ret) {
2191 libnvme_free_ctrl(c);
2192 return ret;
2193 }
2194
2195 *ctrl = c;
2196 return 0;
2197}
2198
2199static int nvmf_create_discovery_ctrl(struct libnvme_global_ctx *ctx,
2200 struct libnvmf_context *fctx, libnvme_host_t h,
2201 struct libnvme_ctrl **ctrl)
2202{
2203 __cleanup_libnvme_free__attribute__((cleanup(libnvme_freep))) struct nvme_id_ctrl *id = NULL((void*)0);
2204 struct libnvme_ctrl *c;
2205 int ret;
2206
2207 ret = __create_discovery_ctrl(ctx, fctx, h, &c);
2208 if (ret)
2209 return ret;
2210
2211 if (libnvme_ctrl_get_unique_discovery_ctrl(c)) {
2212 *ctrl = c;
2213 return 0;
2214 }
2215
2216 id = libnvme_alloc(sizeof(*id));
2217 if (!id) {
2218 libnvme_free_ctrl(c);
2219 return -ENOMEM12;
2220 }
2221
2222 ret = libnvme_open(ctx, c->name, &c->hdl);
2223 if (ret) {
2224 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)
;
2225 return ret;
2226 }
2227
2228 /* Find out the name of discovery controller */
2229 ret = libnvme_ctrl_identify(c, id);
2230 if (ret) {
2231 libnvme_msg(ctx, LIBNVME_LOG_ERR,__libnvme_msg(ctx, LIBNVME_LOG_ERR, ((void*)0), "failed to identify controller, error %s\n"
, libnvme_strerror(-ret))
2232 "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))
2233 libnvme_strerror(-ret))__libnvme_msg(ctx, LIBNVME_LOG_ERR, ((void*)0), "failed to identify controller, error %s\n"
, libnvme_strerror(-ret))
;
2234 libnvmf_disconnect_ctrl(c);
2235 libnvme_free_ctrl(c);
2236 return ret;
2237 }
2238
2239 if (!strcmp(id->subnqn, NVME_DISC_SUBSYS_NAME"nqn.2014-08.org.nvmexpress.discovery")) {
2240 *ctrl = c;
2241 return 0;
2242 }
2243
2244 /*
2245 * The subsysnqn is not the well-known name. Prefer the unique
2246 * subsysnqn over the well-known one.
2247 */
2248 libnvmf_disconnect_ctrl(c);
2249 libnvme_free_ctrl(c);
2250
2251 fctx->ctrl_params.subsysnqn = id->subnqn;
2252 ret = __create_discovery_ctrl(ctx, fctx, h, &c);
2253 if (ret)
2254 return ret;
2255
2256 *ctrl = c;
2257 return 0;
2258}
2259
2260int _discovery_config_json(struct libnvme_global_ctx *ctx,
2261 struct libnvmf_context *fctx, libnvme_host_t h, libnvme_ctrl_t c,
2262 bool_Bool connect, bool_Bool force)
2263{
2264 struct libnvmf_context nfctx = *fctx;
2265 libnvme_ctrl_t cn;
2266 int ret = 0;
2267
2268 nfctx.ctrl_params.transport = libnvme_ctrl_get_transport(c);
2269 nfctx.ctrl_params.traddr = libnvme_ctrl_get_traddr(c);
2270 nfctx.ctrl_params.host_traddr = libnvme_ctrl_get_host_traddr(c);
2271 nfctx.ctrl_params.host_iface = libnvme_ctrl_get_host_iface(c);
2272
2273 if (!nfctx.ctrl_params.transport && !nfctx.ctrl_params.traddr)
2274 return 0;
2275
2276 /* ignore none fabric transports */
2277 if (strcmp(nfctx.ctrl_params.transport, "tcp") &&
2278 strcmp(nfctx.ctrl_params.transport, "rdma") &&
2279 strcmp(nfctx.ctrl_params.transport, "fc"))
2280 return 0;
2281
2282 /* ignore if no host_traddr for fc */
2283 if (!strcmp(nfctx.ctrl_params.transport, "fc")) {
2284 if (!nfctx.ctrl_params.host_traddr) {
2285 libnvme_msg(ctx, LIBNVME_LOG_ERR,__libnvme_msg(ctx, LIBNVME_LOG_ERR, ((void*)0), "host_traddr required for fc\n"
)
2286 "host_traddr required for fc\n")__libnvme_msg(ctx, LIBNVME_LOG_ERR, ((void*)0), "host_traddr required for fc\n"
)
;
2287 return 0;
2288 }
2289 }
2290
2291 /* ignore if host_iface set for any transport other than tcp */
2292 if (!strcmp(nfctx.ctrl_params.transport, "rdma") ||
2293 !strcmp(nfctx.ctrl_params.transport, "fc")) {
2294 if (nfctx.ctrl_params.host_iface) {
2295 libnvme_msg(ctx, LIBNVME_LOG_ERR,__libnvme_msg(ctx, LIBNVME_LOG_ERR, ((void*)0), "host_iface not permitted for rdma or fc\n"
)
2296 "host_iface not permitted for rdma or fc\n")__libnvme_msg(ctx, LIBNVME_LOG_ERR, ((void*)0), "host_iface not permitted for rdma or fc\n"
)
;
2297 return 0;
2298 }
2299 }
2300
2301 nfctx.ctrl_params.trsvcid = libnvme_ctrl_get_trsvcid(c);
2302 if (!nfctx.ctrl_params.trsvcid ||
2303 !strcmp(nfctx.ctrl_params.trsvcid, ""))
2304 nfctx.ctrl_params.trsvcid =
2305 libnvmf_get_default_trsvcid(
2306 nfctx.ctrl_params.transport, true1);
2307
2308 if (force)
2309 nfctx.ctrl_params.subsysnqn = libnvme_ctrl_get_subsysnqn(c);
2310 else
2311 nfctx.ctrl_params.subsysnqn = NVME_DISC_SUBSYS_NAME"nqn.2014-08.org.nvmexpress.discovery";
2312
2313 if (libnvme_ctrl_get_persistent(c))
2314 nfctx.persistent = true1;
2315
2316 if (!force) {
2317 cn = lookup_ctrl(h, &nfctx);
2318 if (cn) {
2319 nfctx.persistent = true1;
2320 _nvmf_discovery(ctx, &nfctx, connect, cn);
2321 return 0;
2322 }
2323 }
2324
2325 ret = nvmf_create_discovery_ctrl(ctx, &nfctx, h, &cn);
2326 if (ret)
2327 return 0;
2328
2329 _nvmf_discovery(ctx, &nfctx, connect, cn);
2330 if (!(fctx->persistent || is_persistent_discovery_ctrl(h, cn)))
2331 ret = libnvmf_disconnect_ctrl(cn);
2332 libnvme_free_ctrl(cn);
2333
2334 return ret;
2335}
2336
2337__libnvme_public__attribute__((visibility("default"))) int libnvmf_discovery_config_json(
2338 struct libnvme_global_ctx *ctx, struct libnvmf_context *fctx,
2339 bool_Bool connect, bool_Bool force)
2340{
2341 const char *hnqn, *hid;
2342 struct libnvme_subsystem *s;
2343 struct libnvme_host *h;
2344 struct libnvme_ctrl *c;
2345 int ret = 0, err;
2346
2347 err = lookup_host(ctx, fctx, &h);
2348 if (err)
2349 return err;
2350
2351 err = setup_connection(fctx, h, false0);
2352 if (err)
2353 return err;
2354
2355 libnvme_for_each_host(ctx, h)for (h = libnvme_first_host(ctx); h != ((void*)0); h = libnvme_next_host
(ctx, h))
{
2356 libnvme_for_each_subsystem(h, s)for (s = libnvme_first_subsystem(h); s != ((void*)0); s = libnvme_next_subsystem
(h, s))
{
2357 hnqn = libnvme_host_get_hostnqn(h);
2358 if (fctx->hostnqn && hnqn &&
2359 strcmp(fctx->hostnqn, hnqn))
2360 continue;
2361 hid = libnvme_host_get_hostid(h);
2362 if (fctx->hostid && hid &&
2363 strcmp(fctx->hostid, hid))
2364 continue;
2365
2366 libnvme_subsystem_for_each_ctrl(s, c)for (c = libnvme_subsystem_first_ctrl(s); c != ((void*)0); c =
libnvme_subsystem_next_ctrl(s, c))
{
2367 err = _discovery_config_json(ctx, fctx, h, c,
2368 connect, force);
2369 if (err) {
2370 libnvme_msg(ctx, LIBNVME_LOG_ERR,__libnvme_msg(ctx, LIBNVME_LOG_ERR, ((void*)0), "failed to connect to hostnqn=%s,nqn=%s,%s\n"
, libnvme_host_get_hostnqn(h), libnvme_subsystem_get_name(s),
libnvme_ctrl_get_traddr(c))
2371 "failed to connect to hostnqn=%s,nqn=%s,%s\n",__libnvme_msg(ctx, LIBNVME_LOG_ERR, ((void*)0), "failed to connect to hostnqn=%s,nqn=%s,%s\n"
, libnvme_host_get_hostnqn(h), libnvme_subsystem_get_name(s),
libnvme_ctrl_get_traddr(c))
2372 libnvme_host_get_hostnqn(h),__libnvme_msg(ctx, LIBNVME_LOG_ERR, ((void*)0), "failed to connect to hostnqn=%s,nqn=%s,%s\n"
, libnvme_host_get_hostnqn(h), libnvme_subsystem_get_name(s),
libnvme_ctrl_get_traddr(c))
2373 libnvme_subsystem_get_name(s),__libnvme_msg(ctx, LIBNVME_LOG_ERR, ((void*)0), "failed to connect to hostnqn=%s,nqn=%s,%s\n"
, libnvme_host_get_hostnqn(h), libnvme_subsystem_get_name(s),
libnvme_ctrl_get_traddr(c))
2374 libnvme_ctrl_get_traddr(c))__libnvme_msg(ctx, LIBNVME_LOG_ERR, ((void*)0), "failed to connect to hostnqn=%s,nqn=%s,%s\n"
, libnvme_host_get_hostnqn(h), libnvme_subsystem_get_name(s),
libnvme_ctrl_get_traddr(c))
;
2375
2376 if (!ret)
2377 ret = err;
2378 }
2379 }
2380 }
2381 }
2382
2383 return ret;
2384}
2385
2386__libnvme_public__attribute__((visibility("default"))) int libnvmf_connect_config_json(struct libnvme_global_ctx *ctx,
2387 struct libnvmf_context *fctx)
2388{
2389 const char *hnqn, *hid;
2390 const char *transport;
2391 libnvme_host_t h;
2392 libnvme_subsystem_t s;
2393 libnvme_ctrl_t c, _c;
2394 int ret = 0, err;
2395
2396 err = lookup_host(ctx, fctx, &h);
2397 if (err)
2398 return err;
2399
2400 err = setup_connection(fctx, h, false0);
2401 if (err)
2402 return err;
2403
2404 libnvme_for_each_host(ctx, h)for (h = libnvme_first_host(ctx); h != ((void*)0); h = libnvme_next_host
(ctx, h))
{
2405 libnvme_for_each_subsystem(h, s)for (s = libnvme_first_subsystem(h); s != ((void*)0); s = libnvme_next_subsystem
(h, s))
{
2406 hnqn = libnvme_host_get_hostnqn(h);
2407 if (fctx->hostnqn && hnqn &&
2408 strcmp(fctx->hostnqn, hnqn))
2409 continue;
2410 hid = libnvme_host_get_hostid(h);
2411 if (fctx->hostid && hid &&
2412 strcmp(fctx->hostid, hid))
2413 continue;
2414
2415 libnvme_subsystem_for_each_ctrl_safe(s, c, _c)for (c = libnvme_subsystem_first_ctrl(s), _c = libnvme_subsystem_next_ctrl
(s, c); c != ((void*)0); c = _c, _c = libnvme_subsystem_next_ctrl
(s, c))
{
2416 transport = libnvme_ctrl_get_transport(c);
2417
2418 /* ignore none fabric transports */
2419 if (strcmp(transport, "tcp") &&
2420 strcmp(transport, "rdma") &&
2421 strcmp(transport, "fc"))
2422 continue;
2423
2424 err = libnvmf_connect_ctrl(c);
2425 if (err) {
2426 if (err == -ENVME_CONNECT_ALREADY)
2427 continue;
2428
2429 libnvme_msg(ctx, LIBNVME_LOG_ERR,__libnvme_msg(ctx, LIBNVME_LOG_ERR, ((void*)0), "failed to connect to hostnqn=%s,nqn=%s,%s\n"
, libnvme_host_get_hostnqn(h), libnvme_subsystem_get_name(s),
libnvme_ctrl_get_traddr(c))
2430 "failed to connect to hostnqn=%s,nqn=%s,%s\n",__libnvme_msg(ctx, LIBNVME_LOG_ERR, ((void*)0), "failed to connect to hostnqn=%s,nqn=%s,%s\n"
, libnvme_host_get_hostnqn(h), libnvme_subsystem_get_name(s),
libnvme_ctrl_get_traddr(c))
2431 libnvme_host_get_hostnqn(h),__libnvme_msg(ctx, LIBNVME_LOG_ERR, ((void*)0), "failed to connect to hostnqn=%s,nqn=%s,%s\n"
, libnvme_host_get_hostnqn(h), libnvme_subsystem_get_name(s),
libnvme_ctrl_get_traddr(c))
2432 libnvme_subsystem_get_name(s),__libnvme_msg(ctx, LIBNVME_LOG_ERR, ((void*)0), "failed to connect to hostnqn=%s,nqn=%s,%s\n"
, libnvme_host_get_hostnqn(h), libnvme_subsystem_get_name(s),
libnvme_ctrl_get_traddr(c))
2433 libnvme_ctrl_get_traddr(c))__libnvme_msg(ctx, LIBNVME_LOG_ERR, ((void*)0), "failed to connect to hostnqn=%s,nqn=%s,%s\n"
, libnvme_host_get_hostnqn(h), libnvme_subsystem_get_name(s),
libnvme_ctrl_get_traddr(c))
;
2434
2435 if (!ret)
2436 ret = err;
2437 }
2438 }
2439 }
2440 }
2441
2442 return ret;
2443}
2444
2445__libnvme_public__attribute__((visibility("default"))) int libnvmf_discovery_config_file(
2446 struct libnvme_global_ctx *ctx, struct libnvmf_context *fctx,
2447 bool_Bool connect, bool_Bool force)
2448{
2449 int err;
2450
2451 err = fctx->hooks.parser_init(fctx, fctx->hooks.user_data);
2452 if (err)
2453 return err;
2454
2455 do {
2456 struct libnvmf_context nfctx = *fctx;
2457 err = fctx->hooks.parser_next_line(&nfctx, fctx->hooks.user_data);
2458 if (err)
2459 break;
2460 libnvmf_discovery(ctx, &nfctx, connect, force);
2461 } while (!err);
2462
2463 fctx->hooks.parser_cleanup(fctx, fctx->hooks.user_data);
2464
2465 if (err != -EOF(-1))
2466 return err;
2467
2468 return 0;
2469}
2470
2471__libnvme_public__attribute__((visibility("default"))) int libnvmf_config_modify(struct libnvme_global_ctx *ctx,
2472 struct libnvmf_context *fctx)
2473{
2474 __cleanup_free__attribute__((cleanup(freep))) char *hnqn = NULL((void*)0);
2475 __cleanup_free__attribute__((cleanup(freep))) char *hid = NULL((void*)0);
2476 struct libnvme_host *h;
2477 struct libnvme_subsystem *s;
2478 struct libnvme_ctrl *c;
2479
2480 if (!fctx->hostnqn)
2481 fctx->hostnqn = hnqn = libnvme_read_hostnqn();
2482 if (!fctx->hostid && hnqn)
2483 fctx->hostid = hid = libnvme_read_hostid();
Although the value stored to 'hid' is used in the enclosing expression, the value is never actually read from 'hid'
2484
2485 h = libnvme_lookup_host(ctx, fctx->hostnqn, fctx->hostid);
2486 if (!h) {
2487 libnvme_msg(ctx, LIBNVME_LOG_ERR, "Failed to lookup host '%s'\n",__libnvme_msg(ctx, LIBNVME_LOG_ERR, ((void*)0), "Failed to lookup host '%s'\n"
, fctx->hostnqn)
2488 fctx->hostnqn)__libnvme_msg(ctx, LIBNVME_LOG_ERR, ((void*)0), "Failed to lookup host '%s'\n"
, fctx->hostnqn)
;
2489 return -ENODEV19;
2490 }
2491
2492 if (fctx->hostkey)
2493 libnvme_host_set_dhchap_host_key(h, fctx->hostkey);
2494
2495 s = libnvme_lookup_subsystem(h, NULL((void*)0), fctx->ctrl_params.subsysnqn);
2496 if (!s) {
2497 libnvme_msg(ctx, LIBNVME_LOG_ERR, "Failed to lookup subsystem '%s'\n",__libnvme_msg(ctx, LIBNVME_LOG_ERR, ((void*)0), "Failed to lookup subsystem '%s'\n"
, fctx->ctrl_params.subsysnqn)
2498 fctx->ctrl_params.subsysnqn)__libnvme_msg(ctx, LIBNVME_LOG_ERR, ((void*)0), "Failed to lookup subsystem '%s'\n"
, fctx->ctrl_params.subsysnqn)
;
2499 return -ENODEV19;
2500 }
2501
2502 c = libnvme_lookup_ctrl(s, &fctx->ctrl_params, NULL((void*)0));
2503 if (!c) {
2504 libnvme_msg(ctx, LIBNVME_LOG_ERR, "Failed to lookup controller\n")__libnvme_msg(ctx, LIBNVME_LOG_ERR, ((void*)0), "Failed to lookup controller\n"
)
;
2505 return -ENODEV19;
2506 }
2507 if (fctx->ctrlkey)
2508 libnvme_ctrl_set_dhchap_ctrl_key(c, fctx->ctrlkey);
2509
2510 nvme_parse_tls_args(fctx->keyring, fctx->tls_key,
2511 fctx->tls_key_identity, &fctx->ctrl_params.cfg, c);
2512
2513 update_config(c, &fctx->ctrl_params.cfg);
2514
2515 return 0;
2516}
2517
2518#define NBFT_SYSFS_FILENAME"NBFT*" "NBFT*"
2519
2520static int nbft_filter(const struct dirent *dent)
2521{
2522 return !fnmatch(NBFT_SYSFS_FILENAME"NBFT*", dent->d_name, FNM_PATHNAME(1 << 0));
2523}
2524
2525__libnvme_public__attribute__((visibility("default"))) int libnvmf_nbft_read_files(
2526 struct libnvme_global_ctx *ctx, char *path,
2527 struct nbft_file_entry **head)
2528{
2529 struct nbft_file_entry *entry = NULL((void*)0);
2530 struct libnbft_info *nbft;
2531 struct dirent **dent;
2532 char filename[PATH_MAX4096];
2533 int i, count, ret;
2534
2535 count = scandir(path, &dent, nbft_filter, NULL((void*)0));
2536 if (count < 0)
2537 return -errno(*__errno_location ());
2538
2539 for (i = 0; i < count; i++) {
2540 snprintf(filename, sizeof(filename), "%s/%s", path,
2541 dent[i]->d_name);
2542
2543 ret = libnvmf_read_nbft(ctx, &nbft, filename);
2544 if (!ret) {
2545 struct nbft_file_entry *new;
2546
2547 new = calloc(1, sizeof(*new));
2548 if (!new)
2549 return -ENOMEM12;
2550 new->nbft = nbft;
2551 if (entry) {
2552 entry->next = new;
2553 entry = entry->next;
2554 } else {
2555 entry = new;
2556 *head = entry;
2557 }
2558 }
2559 free(dent[i]);
2560 }
2561 free(dent);
2562 return 0;
2563}
2564
2565__libnvme_public__attribute__((visibility("default"))) void libnvmf_nbft_free(
2566 struct libnvme_global_ctx *ctx, struct nbft_file_entry *head)
2567{
2568 if (!head)
2569 return;
2570
2571 while (head) {
2572 struct nbft_file_entry *next = head->next;
2573
2574 libnvmf_free_nbft(ctx, head->nbft);
2575 free(head);
2576
2577 head = next;
2578 }
2579}
2580
2581static bool_Bool validate_uri(struct libnvme_global_ctx *ctx,
2582 struct libnbft_discovery *dd,
2583 struct libnvmf_uri *uri)
2584{
2585 if (!uri) {
2586 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)
2587 "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)
2588 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)
;
2589 return false0;
2590 }
2591 if (strcmp(uri->scheme, "nvme") != 0) {
2592 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)
2593 "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)
2594 dd->index, uri->scheme)__libnvme_msg(ctx, LIBNVME_LOG_ERR, ((void*)0), "Discovery Descriptor %d: unsupported scheme '%s'\n"
, dd->index, uri->scheme)
;
2595 return false0;
2596 }
2597 if (!uri->protocol || strcmp(uri->protocol, "tcp") != 0) {
2598 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)
2599 "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)
2600 dd->index, uri->protocol)__libnvme_msg(ctx, LIBNVME_LOG_ERR, ((void*)0), "Discovery Descriptor %d: unsupported transport '%s'\n"
, dd->index, uri->protocol)
;
2601 return false0;
2602 }
2603
2604 return true1;
2605}
2606
2607static int nbft_connect(struct libnvme_global_ctx *ctx,
2608 struct libnvmf_context *fctx, struct libnvme_host *h,
2609 struct nvmf_disc_log_entry *e,
2610 struct libnbft_subsystem_ns *ss)
2611{
2612 libnvme_ctrl_t c;
2613 int saved_log_level;
2614 bool_Bool saved_log_tstamp;
2615 bool_Bool saved_log_pid;
2616 int ret;
2617
2618 saved_log_level = libnvme_get_logging_level(ctx, &saved_log_tstamp,
2619 &saved_log_pid);
2620
2621 c = lookup_ctrl(h, fctx);
2622 if (c && libnvme_ctrl_get_name(c))
2623 return 0;
2624
2625 ret = libnvme_create_ctrl(ctx, &fctx->ctrl_params, &c);
2626 if (ret)
2627 return ret;
2628
2629 /* Pause logging for unavailable SSNSs */
2630 if (ss && ss->unavailable && saved_log_level < 1)
2631 libnvme_set_logging_level(ctx, -1, false0, false0);
2632
2633 /* Update tls or concat */
2634 nvmf_update_tls_concat(e, c, h);
2635
2636 ret = libnvmf_add_ctrl(h, c);
2637
2638 /* Resume logging */
2639 if (ss && ss->unavailable && saved_log_level < 1)
2640 libnvme_set_logging_level(ctx,
2641 saved_log_level,
2642 saved_log_pid,
2643 saved_log_tstamp);
2644
2645 if (ret) {
2646 libnvme_free_ctrl(c);
2647 /*
2648 * In case this SSNS was marked as 'unavailable' and
2649 * our connection attempt has failed, ignore it.
2650 */
2651 if (ss && ss->unavailable) {
2652 libnvme_msg(ctx, LIBNVME_LOG_INFO,__libnvme_msg(ctx, LIBNVME_LOG_INFO, ((void*)0), "SSNS %d reported as unavailable, skipping\n"
, ss->index)
2653 "SSNS %d reported as unavailable, skipping\n",__libnvme_msg(ctx, LIBNVME_LOG_INFO, ((void*)0), "SSNS %d reported as unavailable, skipping\n"
, ss->index)
2654 ss->index)__libnvme_msg(ctx, LIBNVME_LOG_INFO, ((void*)0), "SSNS %d reported as unavailable, skipping\n"
, ss->index)
;
2655 return 0;
2656 }
2657 return ret;
2658 }
2659
2660 if (fctx->hooks.connected)
2661 fctx->hooks.connected(fctx, c, fctx->hooks.user_data);
2662
2663 return 0;
2664}
2665
2666static int nbft_discovery(struct libnvme_global_ctx *ctx,
2667 struct libnvmf_context *fctx, struct libnbft_discovery *dd,
2668 struct libnvme_host *h, struct libnvme_ctrl *c)
2669{
2670 struct nvmf_discovery_log *log = NULL((void*)0);
2671 int ret;
2672 int i;
2673
2674 struct libnvmf_discovery_args args = {
2675 .max_retries = 10 /* MAX_DISC_RETRIES */,
2676 .lsp = NVMF_LOG_DISC_LSP_NONE,
2677 };
2678
2679 ret = nvme_discovery_log(c, &args, &log);
2680 if (ret) {
2681 libnvme_msg(ctx, LIBNVME_LOG_ERR,__libnvme_msg(ctx, LIBNVME_LOG_ERR, ((void*)0), "Discovery Descriptor %d: failed to get discovery log: %s\n"
, dd->index, libnvme_strerror(ret))
2682 "Discovery Descriptor %d: failed to get discovery log: %s\n",__libnvme_msg(ctx, LIBNVME_LOG_ERR, ((void*)0), "Discovery Descriptor %d: failed to get discovery log: %s\n"
, dd->index, libnvme_strerror(ret))
2683 dd->index, libnvme_strerror(ret))__libnvme_msg(ctx, LIBNVME_LOG_ERR, ((void*)0), "Discovery Descriptor %d: failed to get discovery log: %s\n"
, dd->index, libnvme_strerror(ret))
;
2684 return ret;
2685 }
2686
2687 for (i = 0; i < le64_to_cpu(log->numrec); i++) {
2688 struct nvmf_disc_log_entry *e = &log->entries[i];
2689 struct libnvmf_context nfctx = *fctx;
2690 libnvme_ctrl_t cl;
2691 int tmo = fctx->ctrl_params.cfg.keep_alive_tmo;
2692
2693 sanitize_discovery_log_entry(c->ctx, e);
2694
2695 nfctx.ctrl_params.subsysnqn = e->subnqn;
2696 nfctx.ctrl_params.transport = libnvmf_trtype_str(e->trtype);
2697 nfctx.ctrl_params.traddr = e->traddr;
2698 nfctx.ctrl_params.trsvcid = e->trsvcid;
2699
2700 if (e->subtype == NVME_NQN_CURR)
2701 continue;
2702
2703 /* Already connected ? */
2704 cl = lookup_ctrl(h, &nfctx);
2705 if (cl && libnvme_ctrl_get_name(cl))
2706 continue;
2707
2708 /* Skip connect if the transport types don't match */
2709 if (strcmp(libnvme_ctrl_get_transport(c),
2710 nfctx.ctrl_params.transport))
2711 continue;
2712
2713 if (e->subtype == NVME_NQN_DISC) {
2714 libnvme_ctrl_t child;
2715
2716 ret = nvmf_connect_disc_entry(h, e, &nfctx,
2717 NULL((void*)0), &child);
2718 if (ret)
2719 continue;
2720 nbft_discovery(ctx, &nfctx, dd, h, child);
2721 libnvmf_disconnect_ctrl(child);
2722 libnvme_free_ctrl(child);
2723 } else {
2724 ret = nbft_connect(ctx, &nfctx, h, e, NULL((void*)0));
2725
2726 /*
2727 * With TCP/DHCP, it can happen that the OS
2728 * obtains a different local IP address than the
2729 * firmware had. Retry without host_traddr.
2730 */
2731 if (ret == -ENVME_CONNECT_ADDRNOTAVAIL &&
2732 !strcmp(nfctx.ctrl_params.transport, "tcp") &&
2733 strlen(dd->hfi->tcp_info.dhcp_server_ipaddr) > 0) {
2734 const char *htradr =
2735 nfctx.ctrl_params.host_traddr;
2736
2737 nfctx.ctrl_params.host_traddr = NULL((void*)0);
2738 ret = nbft_connect(ctx, &nfctx, h, e, NULL((void*)0));
2739
2740 if (ret == 0)
2741 libnvme_msg(ctx, LIBNVME_LOG_INFO,__libnvme_msg(ctx, LIBNVME_LOG_INFO, ((void*)0), "Discovery Descriptor %d: connect with host_traddr=\"%s\" failed, success after omitting host_traddr\n"
, dd->index, htradr)
2742 "Discovery Descriptor %d: connect with host_traddr=\"%s\" failed, success after omitting host_traddr\n",__libnvme_msg(ctx, LIBNVME_LOG_INFO, ((void*)0), "Discovery Descriptor %d: connect with host_traddr=\"%s\" failed, success after omitting host_traddr\n"
, dd->index, htradr)
2743 dd->index,__libnvme_msg(ctx, LIBNVME_LOG_INFO, ((void*)0), "Discovery Descriptor %d: connect with host_traddr=\"%s\" failed, success after omitting host_traddr\n"
, dd->index, htradr)
2744 htradr)__libnvme_msg(ctx, LIBNVME_LOG_INFO, ((void*)0), "Discovery Descriptor %d: connect with host_traddr=\"%s\" failed, success after omitting host_traddr\n"
, dd->index, htradr)
;
2745 }
2746
2747 if (ret)
2748 libnvme_msg(ctx, LIBNVME_LOG_ERR,__libnvme_msg(ctx, LIBNVME_LOG_ERR, ((void*)0), "Discovery Descriptor %d: no controller found\n"
, dd->index)
2749 "Discovery Descriptor %d: no controller found\n",__libnvme_msg(ctx, LIBNVME_LOG_ERR, ((void*)0), "Discovery Descriptor %d: no controller found\n"
, dd->index)
2750 dd->index)__libnvme_msg(ctx, LIBNVME_LOG_ERR, ((void*)0), "Discovery Descriptor %d: no controller found\n"
, dd->index)
;
2751 if (ret == -ENOMEM12)
2752 break;
2753 }
2754
2755 fctx->ctrl_params.cfg.keep_alive_tmo = tmo;
2756 }
2757
2758 libnvme_free(log);
2759 return 0;
2760}
2761
2762#define VLAN_PROC_PATH"/proc/net/vlan" "/proc/net/vlan"
2763
2764/*
2765 * Return 0 for no vlan_id, to be consistent with the NBFT spec.
2766 */
2767static int get_vlan_id(const char *ifname)
2768{
2769 char path[256], line[256];
2770 int vlan_id = 0;
2771 FILE *f;
2772
2773 snprintf(path, sizeof(path), "%s/%s", VLAN_PROC_PATH"/proc/net/vlan", ifname);
2774 f = fopen(path, "r");
2775 if (!f)
2776 return 0;
2777
2778 while (fgets(line, sizeof(line), f)) {
2779 if (sscanf(line, " VID: %d", &vlan_id) == 1) {
2780 fclose(f);
2781 return vlan_id;
2782 }
2783 }
2784
2785 fclose(f);
2786 return 0;
2787}
2788
2789/*
2790 * Find network interface corresponding to the NBFT HFI
2791 * by looking for mac address and vlan id.
2792 */
2793static char *nbft_find_hfi_iface(struct libnbft_hfi *hfi)
2794{
2795 struct ifaddrs *ifaddr, *ifa;
2796 char *result = NULL((void*)0);
2797
2798 if (strcmp((char *)hfi->transport, "tcp"))
2799 return NULL((void*)0);
2800
2801 if (getifaddrs(&ifaddr) != 0)
2802 return NULL((void*)0);
2803
2804 for (ifa = ifaddr; ifa != NULL((void*)0); ifa = ifa->ifa_next) {
2805 struct sockaddr_ll *sll;
2806
2807 if (!ifa->ifa_addr)
2808 continue;
2809
2810 if (ifa->ifa_addr->sa_family != AF_PACKET17)
2811 continue;
2812
2813 sll = (struct sockaddr_ll *)ifa->ifa_addr;
2814
2815 if (sll->sll_halen != ETH_ALEN6)
2816 continue;
2817
2818 if (!memcmp(sll->sll_addr, hfi->tcp_info.mac_addr, ETH_ALEN6)) {
2819 int vlan_id = get_vlan_id(ifa->ifa_name);
2820
2821 if (vlan_id == hfi->tcp_info.vlan) {
2822 result = strdup(ifa->ifa_name);
2823 break;
2824 }
2825 }
2826 }
2827
2828 freeifaddrs(ifaddr);
2829 return result;
2830}
2831
2832__libnvme_public__attribute__((visibility("default"))) int libnvmf_discovery_nbft(struct libnvme_global_ctx *ctx,
2833 struct libnvmf_context *fctx, bool_Bool connect, char *nbft_path)
2834{
2835 const char *hostnqn = NULL((void*)0), *hostid = NULL((void*)0), *host_traddr = NULL((void*)0);
2836 char uuid[NVME_UUID_LEN_STRING37];
2837 struct nbft_file_entry *entry = NULL((void*)0);
2838 struct libnbft_subsystem_ns **ss;
2839 struct libnbft_hfi *hfi;
2840 struct libnbft_discovery **dd;
2841 struct libnvme_host *h;
2842 int ret, rr, i;
2843
2844 ret = lookup_host(ctx, fctx, &h);
2845 if (ret)
2846 return ret;
2847
2848 ret = setup_connection(fctx, h, false0);
2849 if (ret)
2850 return ret;
2851
2852 if (!connect)
2853 /* TODO: print discovery-type info from NBFT tables */
2854 return 0;
2855
2856 ret = libnvmf_nbft_read_files(ctx, nbft_path, &entry);
2857 if (ret) {
2858 if (ret != -ENOENT2)
2859 libnvme_msg(ctx, LIBNVME_LOG_ERR,__libnvme_msg(ctx, LIBNVME_LOG_ERR, ((void*)0), "Failed to access ACPI tables directory\n"
)
2860 "Failed to access ACPI tables directory\n")__libnvme_msg(ctx, LIBNVME_LOG_ERR, ((void*)0), "Failed to access ACPI tables directory\n"
)
;
2861 else
2862 ret = 0; /* nothing to connect */
2863 goto out_free;
2864 }
2865
2866 for (; entry; entry = entry->next) {
2867 if (fctx->hostnqn)
2868 hostnqn = fctx->hostnqn;
2869 else {
2870 hostnqn = entry->nbft->host.nqn;
2871 if (!hostnqn)
2872 hostnqn = fctx->hostnqn;
2873 }
2874
2875 if (fctx->hostid)
2876 hostid = fctx->hostid;
2877 else if (*entry->nbft->host.id) {
2878 ret = libnvme_uuid_to_string(entry->nbft->host.id, uuid);
2879 if (!ret)
2880 hostid = uuid;
2881 else
2882 hostid = fctx->hostid;
2883 }
2884
2885 h = libnvme_lookup_host(ctx, hostnqn, hostid);
2886 if (!h) {
2887 ret = -ENOENT2;
2888 goto out_free;
2889 }
2890
2891 /* Subsystem Namespace Descriptor List */
2892 for (ss = entry->nbft->subsystem_ns_list; ss && *ss; ss++)
2893 for (i = 0; i < (*ss)->num_hfis; i++) {
2894 struct libnvmf_context nfctx = *fctx;
2895
2896 hfi = (*ss)->hfis[i];
2897
2898 /* Skip discovery NQN records */
2899 if (strcmp((*ss)->subsys_nqn,
2900 NVME_DISC_SUBSYS_NAME"nqn.2014-08.org.nvmexpress.discovery") == 0) {
2901 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)
2902 "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)
2903 (*ss)->index)__libnvme_msg(ctx, LIBNVME_LOG_INFO, ((void*)0), "SSNS %d points to well-known discovery NQN, skipping\n"
, (*ss)->index)
;
2904 continue;
2905 }
2906
2907 nfctx.ctrl_params.host_traddr = NULL((void*)0);
2908 if (!fctx->ctrl_params.host_traddr &&
2909 !strncmp((*ss)->transport, "tcp", 3))
2910 nfctx.ctrl_params.host_traddr =
2911 hfi->tcp_info.ipaddr;
2912
2913 nfctx.ctrl_params.subsysnqn = (*ss)->subsys_nqn;
2914 nfctx.ctrl_params.transport = (*ss)->transport;
2915 nfctx.ctrl_params.traddr = (*ss)->traddr;
2916 nfctx.ctrl_params.trsvcid = (*ss)->trsvcid;
2917 nfctx.ctrl_params.host_iface = nbft_find_hfi_iface(hfi);
2918 if (!nfctx.ctrl_params.host_iface)
2919 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)
2920 "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)
2921 (*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)
;
2922
2923 rr = nbft_connect(ctx, &nfctx, h, NULL((void*)0), *ss);
2924
2925 /*
2926 * With TCP/DHCP, it can happen that the OS
2927 * obtains a different local IP address than the
2928 * firmware had. Retry without host_traddr.
2929 */
2930 if (rr == -ENVME_CONNECT_ADDRNOTAVAIL &&
2931 !strcmp(nfctx.ctrl_params.transport,
2932 "tcp") &&
2933 strlen(hfi->tcp_info.dhcp_server_ipaddr) > 0) {
2934 nfctx.ctrl_params.host_traddr = NULL((void*)0);
2935
2936 rr = nbft_connect(ctx, &nfctx, h, NULL((void*)0),
2937 *ss);
2938
2939 if (rr == 0)
2940 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)
2941 "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)
2942 (*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)
2943 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)
;
2944 }
2945
2946 if (nfctx.ctrl_params.host_iface)
2947 free((char *)nfctx.ctrl_params.host_iface);
2948
2949 if (rr) {
2950 libnvme_msg(ctx, LIBNVME_LOG_ERR,__libnvme_msg(ctx, LIBNVME_LOG_ERR, ((void*)0), "SSNS %d: no controller found\n"
, (*ss)->index)
2951 "SSNS %d: no controller found\n",__libnvme_msg(ctx, LIBNVME_LOG_ERR, ((void*)0), "SSNS %d: no controller found\n"
, (*ss)->index)
2952 (*ss)->index)__libnvme_msg(ctx, LIBNVME_LOG_ERR, ((void*)0), "SSNS %d: no controller found\n"
, (*ss)->index)
;
2953 /* report an error */
2954 ret = rr;
2955 }
2956
2957 if (rr == -ENOMEM12)
2958 goto out_free;
2959 }
2960
2961 /* Discovery Descriptor List */
2962 for (dd = entry->nbft->discovery_list; dd && *dd; dd++) {
2963 __cleanup_uri__attribute__((cleanup(free_uri))) struct libnvmf_uri *uri = NULL((void*)0);
2964 __cleanup_free__attribute__((cleanup(freep))) char *trsvcid = NULL((void*)0);
2965 struct libnvmf_context nfctx = *fctx;
2966 bool_Bool persistent = false0;
2967 bool_Bool linked = false0;
2968 libnvme_ctrl_t c;
2969
2970 /* only perform discovery when no SSNS record references it */
2971 for (ss = entry->nbft->subsystem_ns_list;
2972 ss && *ss; ss++)
2973 if ((*ss)->discovery &&
2974 (*ss)->discovery->index == (*dd)->index &&
2975 /* unavailable boot attempts are not discovered
2976 * and may get transferred along with a well-known
2977 * discovery NQN into an SSNS record.
2978 */
2979 strcmp((*ss)->subsys_nqn,
2980 NVME_DISC_SUBSYS_NAME"nqn.2014-08.org.nvmexpress.discovery") != 0) {
2981 linked = true1;
2982 break;
2983 }
2984 if (linked)
2985 continue;
2986
2987 hfi = (*dd)->hfi;
2988 ret = libnvmf_uri_parse((*dd)->uri, &uri);
2989 if (ret)
2990 continue;
2991 if (!validate_uri(ctx, *dd, uri))
2992 continue;
2993
2994 host_traddr = NULL((void*)0);
2995 if (!fctx->ctrl_params.host_traddr &&
2996 !strncmp(uri->protocol, "tcp", 3))
2997 host_traddr = hfi->tcp_info.ipaddr;
2998 if (uri->port > 0) {
2999 if (asprintf(&trsvcid, "%d", uri->port) < 0) {
3000 ret = -ENOMEM12;
3001 goto out_free;
3002 }
3003 } else
3004 trsvcid =
3005 strdup(libnvmf_get_default_trsvcid(
3006 uri->protocol, true1));
3007
3008 nfctx.ctrl_params.subsysnqn = NVME_DISC_SUBSYS_NAME"nqn.2014-08.org.nvmexpress.discovery";
3009 nfctx.ctrl_params.transport = uri->protocol;
3010 nfctx.ctrl_params.traddr = uri->host;
3011 nfctx.ctrl_params.trsvcid = trsvcid;
3012 nfctx.ctrl_params.host_traddr = host_traddr;
3013 nfctx.ctrl_params.host_iface = nbft_find_hfi_iface(hfi);
3014 if (!nfctx.ctrl_params.host_iface)
3015 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)
3016 "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)
3017 (*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)
;
3018
3019 /* Lookup existing discovery controller */
3020 c = lookup_ctrl(h, &nfctx);
3021 if (c && libnvme_ctrl_get_name(c))
3022 persistent = true1;
3023
3024 if (!c) {
3025 ret = nvmf_create_discovery_ctrl(ctx, &nfctx,
3026 h, &c);
3027 if (ret == -ENVME_CONNECT_ADDRNOTAVAIL &&
3028 !strcmp(nfctx.ctrl_params.transport,
3029 "tcp") &&
3030 strlen(hfi->tcp_info.dhcp_server_ipaddr) > 0) {
3031 nfctx.ctrl_params.traddr = NULL((void*)0);
3032 ret = nvmf_create_discovery_ctrl(ctx,
3033 &nfctx, h, &c);
3034 }
3035 } else
3036 ret = 0;
3037
3038 if (nfctx.ctrl_params.host_iface)
3039 free((char *)nfctx.ctrl_params.host_iface);
3040
3041 if (ret) {
3042 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))
3043 "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))
3044 (*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))
;
3045 goto out_free;
3046 }
3047
3048 rr = nbft_discovery(ctx, &nfctx, *dd, h, c);
3049 if (!persistent)
3050 libnvmf_disconnect_ctrl(c);
3051 libnvme_free_ctrl(c);
3052 if (rr == -ENOMEM12) {
3053 ret = rr;
3054 goto out_free;
3055 }
3056 }
3057 }
3058out_free:
3059 libnvmf_nbft_free(ctx, entry);
3060 return ret;
3061}
3062
3063__libnvme_public__attribute__((visibility("default"))) int libnvmf_discovery(
3064 struct libnvme_global_ctx *ctx, struct libnvmf_context *fctx,
3065 bool_Bool connect, bool_Bool force)
3066{
3067 struct libnvme_ctrl *c = NULL((void*)0);
3068 struct libnvme_host *h;
3069 int ret;
3070
3071 ret = lookup_host(ctx, fctx, &h);
3072 if (ret)
3073 return ret;
3074
3075 ret = setup_connection(fctx, h, true1);
3076 if (ret)
3077 return ret;
3078
3079 if (fctx->device && !force) {
3080 ret = libnvme_scan_ctrl(ctx, fctx->device, &c);
3081 if (!ret) {
3082 /* Check if device matches command-line options */
3083 if (!libnvmf_ctrl_match_config(c, fctx)) {
3084 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)
3085 "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)
3086 fctx->device)__libnvme_msg(ctx, LIBNVME_LOG_ERR, ((void*)0), "ctrl device %s found, ignoring non matching command-line options\n"
, fctx->device)
;
3087 }
3088
3089 if (!libnvme_ctrl_get_discovery_ctrl(c)) {
3090 libnvme_msg(__libnvme_msg(ctx, LIBNVME_LOG_ERR, ((void*)0), "ctrl device %s found, ignoring non discovery controller\n"
, fctx->device)
3091 ctx, LIBNVME_LOG_ERR,__libnvme_msg(ctx, LIBNVME_LOG_ERR, ((void*)0), "ctrl device %s found, ignoring non discovery controller\n"
, fctx->device)
3092 "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)
3093 fctx->device)__libnvme_msg(ctx, LIBNVME_LOG_ERR, ((void*)0), "ctrl device %s found, ignoring non discovery controller\n"
, fctx->device)
;
3094
3095 libnvme_free_ctrl(c);
3096 c = NULL((void*)0);
3097 fctx->persistent = false0;
3098 } else {
3099 /*
3100 * If the controller device is found it must
3101 * be persistent, and shouldn't be disconnected
3102 * on exit.
3103 */
3104 fctx->persistent = true1;
3105 /*
3106 * When --host-traddr/--host-iface are not specified on the
3107 * command line, use the discovery controller's (c) host-
3108 * traddr/host-iface for the connections to controllers
3109 * returned in the Discovery Log Pages. This is essential
3110 * when invoking "connect-all" with --device to reuse an
3111 * existing persistent discovery controller (as is done
3112 * for the udev rules). This ensures that host-traddr/
3113 * host-iface are consistent with the discovery controller (c).
3114 */
3115 if (!fctx->ctrl_params.host_traddr)
3116 fctx->ctrl_params.host_traddr = (char *)
3117 libnvme_ctrl_get_host_traddr(c);
3118 if (!fctx->ctrl_params.host_iface)
3119 fctx->ctrl_params.host_iface = (char *)
3120 libnvme_ctrl_get_host_iface(c);
3121 }
3122 } else {
3123 /*
3124 * No controller found, fall back to create one.
3125 * But that controller cannot be persistent.
3126 */
3127 libnvme_msg(ctx, LIBNVME_LOG_ERR,__libnvme_msg(ctx, LIBNVME_LOG_ERR, ((void*)0), "ctrl device %s not found%s\n"
, fctx->device, fctx->persistent ? ", ignoring --persistent"
: "")
3128 "ctrl device %s not found%s\n", fctx->device,__libnvme_msg(ctx, LIBNVME_LOG_ERR, ((void*)0), "ctrl device %s not found%s\n"
, fctx->device, fctx->persistent ? ", ignoring --persistent"
: "")
3129 fctx->persistent ? ", ignoring --persistent" : "")__libnvme_msg(ctx, LIBNVME_LOG_ERR, ((void*)0), "ctrl device %s not found%s\n"
, fctx->device, fctx->persistent ? ", ignoring --persistent"
: "")
;
3130 fctx->persistent = false0;
3131 }
3132 }
3133
3134 if (!c && !force) {
3135 c = lookup_ctrl(h, fctx);
3136 if (c)
3137 fctx->persistent = true1;
3138 }
3139 if (!c) {
3140 /* No device or non-matching device, create a new controller */
3141 ret = nvmf_create_discovery_ctrl(ctx, fctx, h, &c);
3142 if (ret) {
3143 if (ret != -ENVME_CONNECT_IGNORED)
3144 libnvme_msg(ctx, LIBNVME_LOG_ERR,__libnvme_msg(ctx, LIBNVME_LOG_ERR, ((void*)0), "failed to add controller, error %s\n"
, libnvme_strerror(-ret))
3145 "failed to add controller, error %s\n",__libnvme_msg(ctx, LIBNVME_LOG_ERR, ((void*)0), "failed to add controller, error %s\n"
, libnvme_strerror(-ret))
3146 libnvme_strerror(-ret))__libnvme_msg(ctx, LIBNVME_LOG_ERR, ((void*)0), "failed to add controller, error %s\n"
, libnvme_strerror(-ret))
;
3147 return ret;
3148 }
3149 }
3150
3151 ret = _nvmf_discovery(ctx, fctx, connect, c);
3152 if (!(fctx->persistent || is_persistent_discovery_ctrl(h, c)))
3153 libnvmf_disconnect_ctrl(c);
3154 libnvme_free_ctrl(c);
3155
3156 return ret;
3157}
3158
3159__libnvme_public__attribute__((visibility("default"))) int libnvmf_connect(
3160 struct libnvme_global_ctx *ctx, struct libnvmf_context *fctx)
3161{
3162 struct libnvme_host *h;
3163 struct libnvme_ctrl *c;
3164 int err;
3165
3166 err = lookup_host(ctx, fctx, &h);
3167 if (err)
3168 return err;
3169
3170 err = setup_connection(fctx, h, false0);
3171 if (err)
3172 return err;
3173
3174 c = lookup_ctrl(h, fctx);
3175 if (c && libnvme_ctrl_get_name(c) &&
3176 !fctx->ctrl_params.cfg.duplicate_connect) {
3177 fctx->hooks.already_connected(fctx, h,
3178 libnvme_ctrl_get_subsysnqn(c),
3179 libnvme_ctrl_get_transport(c),
3180 libnvme_ctrl_get_traddr(c),
3181 libnvme_ctrl_get_trsvcid(c), fctx->hooks.user_data);
3182 return -EALREADY114;
3183 }
3184
3185 err = libnvme_create_ctrl(ctx, &fctx->ctrl_params, &c);
3186 if (err)
3187 return err;
3188
3189 if (fctx->hostkey) {
3190 libnvme_ctrl_set_dhchap_host_key(c, fctx->hostkey);
3191 if (fctx->ctrlkey)
3192 libnvme_ctrl_set_dhchap_ctrl_key(c, fctx->ctrlkey);
3193 }
3194
3195 nvme_parse_tls_args(fctx->keyring, fctx->tls_key,
3196 fctx->tls_key_identity, &fctx->ctrl_params.cfg, c);
3197 update_config(c, &fctx->ctrl_params.cfg);
3198
3199 /*
3200 * We are connecting to a discovery controller, so let's treat
3201 * this as a persistent connection and specify a KATO.
3202 */
3203 if (!strcmp(fctx->ctrl_params.subsysnqn, NVME_DISC_SUBSYS_NAME"nqn.2014-08.org.nvmexpress.discovery")) {
3204 fctx->persistent = true1;
3205
3206 set_discovery_kato(fctx);
3207 }
3208
3209 err = libnvme_add_ctrl(fctx, h, c);
3210 if (err) {
3211 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))
3212 libnvme_strerror(-err))__libnvme_msg(ctx, LIBNVME_LOG_ERR, ((void*)0), "could not add new controller: %s\n"
, libnvme_strerror(-err))
;
3213 libnvme_free_ctrl(c);
3214 return err;
3215 }
3216
3217 fctx->hooks.connected(fctx, c, fctx->hooks.user_data);
3218
3219 return 0;
3220}