Bug Summary

File:.build-ci/../libnvme/src/nvme/fabrics.c
Warning:line 2049, column 32
Access to field 'ctx' results in a dereference of a null pointer (loaded from variable 'c')

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) {
17
Assuming 'err' is 0
18
Taking false branch
37
Assuming 'err' is 0
38
Taking false branch
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)
19
Assuming field 'discovery_log' is null
20
Taking false branch
39
Assuming field 'discovery_log' is null
40
Taking false branch
2034 fctx->hooks.discovery_log(fctx, connect, log, numrec,
2035 fctx->hooks.user_data);
2036
2037 if (!connect
40.1
'connect' is true
)
21
Assuming 'connect' is true
22
Taking false branch
41
Taking false branch
2038 return 0;
2039
2040 for (int i = 0; i < numrec; i++) {
23
Assuming 'i' is < 'numrec'
24
Loop condition is true. Entering loop body
42
Assuming 'i' is < 'numrec'
43
Loop condition is true. Entering loop body
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);
44
Access to field 'ctx' results in a dereference of a null pointer (loaded from variable 'c')
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))
25
Assuming 'cl' is null
2059 continue;
2060
2061 /* Skip connect if the transport types don't match */
2062 if (strcmp(libnvme_ctrl_get_transport(c),
26
Assuming the condition is false
2063 nfctx.ctrl_params.transport))
2064 continue;
2065
2066 if (e->subtype == NVME_NQN_DISC ||
27
Assuming field 'subtype' is not equal to NVME_NQN_DISC
29
Taking false branch
2067 e->subtype == NVME_NQN_CURR) {
28
Assuming field 'subtype' is not equal to 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);
30
Value assigned to 'child'
2100
2101 nfctx.ctrl_params.cfg.keep_alive_tmo = tmo;
2102
2103 if (!child) {
31
Assuming 'child' is null
32
Taking true branch
2104 if (discover)
33
Assuming 'discover' is true
34
Taking true branch
2105 _nvmf_discovery(ctx, &nfctx, true1, child);
35
Passing null pointer value via 4th parameter 'c'
36
Calling '_nvmf_discovery'
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)
1
Assuming field 'transport' is non-null
2274 return 0;
2275
2276 /* ignore none fabric transports */
2277 if (strcmp(nfctx.ctrl_params.transport, "tcp") &&
2
Assuming the condition is false
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")) {
3
Assuming the condition is false
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) {
4
Assuming field 'host_iface' is null
5
Taking false branch
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 ||
6
Assuming field 'trsvcid' is non-null
8
Taking false branch
2303 !strcmp(nfctx.ctrl_params.trsvcid, ""))
7
Assuming the condition is false
2304 nfctx.ctrl_params.trsvcid =
2305 libnvmf_get_default_trsvcid(
2306 nfctx.ctrl_params.transport, true1);
2307
2308 if (force)
9
Assuming 'force' is false
10
Taking false branch
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))
11
Assuming the condition is false
12
Taking false branch
2314 nfctx.persistent = true1;
2315
2316 if (!force
12.1
'force' is false
) {
13
Taking true branch
2317 cn = lookup_ctrl(h, &nfctx);
2318 if (cn) {
14
Assuming 'cn' is non-null
15
Taking true branch
2319 nfctx.persistent = true1;
2320 _nvmf_discovery(ctx, &nfctx, connect, cn);
16
Calling '_nvmf_discovery'
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();
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}