Bug Summary

File:.build-ci/../libnvme/test/../src/nvme/fabrics.c
Warning:line 1174, column 30
Access to field 'transport' 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-redhat-linux-gnu -O3 -analyze -disable-free -clear-ast-before-backend -disable-llvm-verifier -discard-value-names -main-file-name test-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 static -mframe-pointer=none -fmath-errno -ffp-contract=on -fno-rounding-math -mconstructor-aliases -funwind-tables=2 -target-cpu x86-64 -tune-cpu generic -debugger-tuning=gdb -fdebug-compilation-dir=/__w/nvme-cli/nvme-cli/.build-ci -fcoverage-compilation-dir=/__w/nvme-cli/nvme-cli/.build-ci -resource-dir /usr/bin/../lib/clang/22 -include /__w/nvme-cli/nvme-cli/.build-ci/nvme-config.h -I libnvme/test/test-fabrics.p -I libnvme/test -I ../libnvme/test -I . -I .. -I ccan -I ../ccan -I libnvme/src -I ../libnvme/src -I shared -I ../shared -D _FILE_OFFSET_BITS=64 -D _GNU_SOURCE -U NDEBUG -internal-isystem /usr/bin/../lib/clang/22/include -internal-isystem /usr/local/include -internal-isystem /usr/bin/../lib/gcc/x86_64-redhat-linux/16/../../../../x86_64-redhat-linux/include -internal-externc-isystem /include -internal-externc-isystem /usr/include -std=gnu99 -ferror-limit 19 -fgnuc-version=4.2.1 -fgnu89-inline -fskip-odr-check-in-gmf -fcolor-diagnostics -vectorize-loops -vectorize-slp -analyzer-opt-analyze-headers -analyzer-output=html -faddrsig -fdwarf2-cfi-asm -o /__w/nvme-cli/nvme-cli/.build-ci/scan-results/2026-08-08-045408-589-1 -x c ../libnvme/test/test-fabrics.c

../libnvme/test/test-fabrics.c

1// SPDX-License-Identifier: LGPL-2.1-or-later
2/**
3 * This file is part of libnvme.
4 * Copyright (c) 2024 Dell Inc.
5 *
6 * Authors: Martin Belanger <Martin.Belanger@dell.com>
7 *
8 * Unit tests for static helper functions in src/nvme/fabrics.c.
9 *
10 * Accessing static functions requires including the source file directly
11 * because static functions are not exported from the shared library.
12 * Defining 'static' to nothing makes them visible in this translation
13 * unit. The duplicate non-static symbols that fabrics.c also defines
14 * (e.g. trtypes[], arg_str()) are harmless: GNU ld does not report
15 * multiple-definition errors between a .o file and a shared library,
16 * and at run time the main-executable definition takes precedence.
17 */
18#define static /* expose static functions for unit testing */
19#include "../src/nvme/fabrics.c"
20
21#include <stdlib.h>
22
23/* -------------------------------------------------------------------------
24 * Test infrastructure
25 * -------------------------------------------------------------------------
26 */
27static int test_rc;
28
29#define PASS"[PASS]\n" "[PASS]\n"
30#define FAIL"[FAIL]\n" "[FAIL]\n"
31
32#define CHECK(cond, fmt, ...)do { if (cond) { printf(" " fmt " " "[PASS]\n", ...); } else
{ printf(" " fmt " " "[FAIL]\n", ...); test_rc = 1; } } while
(0)
\
33 do { \
34 if (cond) { \
35 printf(" " fmt " " PASS"[PASS]\n", ##__VA_ARGS__); \
36 } else { \
37 printf(" " fmt " " FAIL"[FAIL]\n", ##__VA_ARGS__); \
38 test_rc = EXIT_FAILURE1; \
39 } \
40 } while (0)
41
42/* -------------------------------------------------------------------------
43 * strchomp — strip trailing spaces
44 * -------------------------------------------------------------------------
45 */
46static bool_Bool test_strchomp(void)
47{
48 bool_Bool pass = true1;
49 char s[32];
50
51 printf("\ntest_strchomp:\n");
52
53 strncpy(s, "hello ", sizeof(s));
54 strchomp(s, 8);
55 pass = !strcmp(s, "hello");
56 CHECK(pass, "trailing spaces removed: \"%s\"", s)do { if (pass) { printf(" " "trailing spaces removed: \"%s\""
" " "[PASS]\n", s); } else { printf(" " "trailing spaces removed: \"%s\""
" " "[FAIL]\n", s); test_rc = 1; } } while (0)
;
57
58 strncpy(s, "hello", sizeof(s));
59 strchomp(s, 5);
60 pass = !strcmp(s, "hello");
61 CHECK(pass, "no trailing spaces (unchanged): \"%s\"", s)do { if (pass) { printf(" " "no trailing spaces (unchanged): \"%s\""
" " "[PASS]\n", s); } else { printf(" " "no trailing spaces (unchanged): \"%s\""
" " "[FAIL]\n", s); test_rc = 1; } } while (0)
;
62
63 strncpy(s, " ", sizeof(s));
64 strchomp(s, 3);
65 pass = (s[0] == '\0');
66 CHECK(pass, "all spaces → empty string")do { if (pass) { printf(" " "all spaces → empty string" " "
"[PASS]\n"); } else { printf(" " "all spaces → empty string"
" " "[FAIL]\n"); test_rc = 1; } } while (0)
;
67
68 strncpy(s, "x", sizeof(s));
69 strchomp(s, 0);
70 pass = (s[0] == 'x');
71 CHECK(pass, "max=0 → no change")do { if (pass) { printf(" " "max=0 → no change" " " "[PASS]\n"
); } else { printf(" " "max=0 → no change" " " "[FAIL]\n")
; test_rc = 1; } } while (0)
;
72
73 return pass;
74}
75
76/* -------------------------------------------------------------------------
77 * hostid_from_hostnqn — extract UUID from a hostnqn string
78 * -------------------------------------------------------------------------
79 */
80static bool_Bool test_hostid_from_hostnqn(void)
81{
82 const char *id;
83 bool_Bool pass = true1;
84
85 printf("\ntest_hostid_from_hostnqn:\n");
86
87 id = hostid_from_hostnqn(NULL((void*)0));
88 pass = (id == NULL((void*)0));
89 CHECK(pass, "NULL input → NULL")do { if (pass) { printf(" " "NULL input → NULL" " " "[PASS]\n"
); } else { printf(" " "NULL input → NULL" " " "[FAIL]\n")
; test_rc = 1; } } while (0)
;
90
91 id = hostid_from_hostnqn("nqn.2014-08.org.nvmexpress:no-uuid-here");
92 pass = (id == NULL((void*)0));
93 CHECK(pass, "no uuid: tag → NULL")do { if (pass) { printf(" " "no uuid: tag → NULL" " " "[PASS]\n"
); } else { printf(" " "no uuid: tag → NULL" " " "[FAIL]\n"
); test_rc = 1; } } while (0)
;
94
95 id = hostid_from_hostnqn(
96 "nqn.2014-08.org.nvmexpress:uuid:9ba1651a-ed36-11f0-9858-6c1ff71ba506");
97 pass = (id && !strcmp(id, "9ba1651a-ed36-11f0-9858-6c1ff71ba506"));
98 CHECK(pass, "valid NQN → UUID \"%s\"", id ? id : "(null)")do { if (pass) { printf(" " "valid NQN → UUID \"%s\"" " " "[PASS]\n"
, id ? id : "(null)"); } else { printf(" " "valid NQN → UUID \"%s\""
" " "[FAIL]\n", id ? id : "(null)"); test_rc = 1; } } while (
0)
;
99
100 id = hostid_from_hostnqn("prefix:uuid:abc-123");
101 pass = (id && !strcmp(id, "abc-123"));
102 CHECK(pass, "arbitrary prefix → \"abc-123\"")do { if (pass) { printf(" " "arbitrary prefix → \"abc-123\""
" " "[PASS]\n"); } else { printf(" " "arbitrary prefix → \"abc-123\""
" " "[FAIL]\n"); test_rc = 1; } } while (0)
;
103
104 return pass;
105}
106
107/* -------------------------------------------------------------------------
108 * __add_bool_argument — append a boolean flag to an argument string
109 * -------------------------------------------------------------------------
110 */
111static bool_Bool test_add_bool_argument(void)
112{
113 char *argstr;
114 bool_Bool pass = true1;
115 int ret;
116
117 printf("\ntest___add_bool_argument:\n");
118
119 argstr = strdup("start");
120
121 ret = __add_bool_argument(&argstr, "flag", false0);
122 pass = (ret == 0 && !strcmp(argstr, "start"));
123 CHECK(pass, "arg=false → no change: \"%s\"", argstr)do { if (pass) { printf(" " "arg=false → no change: \"%s\""
" " "[PASS]\n", argstr); } else { printf(" " "arg=false → no change: \"%s\""
" " "[FAIL]\n", argstr); test_rc = 1; } } while (0)
;
124
125 ret = __add_bool_argument(&argstr, "flag", true1);
126 pass = (ret == 0 && !strcmp(argstr, "start,flag"));
127 CHECK(pass, "arg=true → appended: \"%s\"", argstr)do { if (pass) { printf(" " "arg=true → appended: \"%s\"" " "
"[PASS]\n", argstr); } else { printf(" " "arg=true → appended: \"%s\""
" " "[FAIL]\n", argstr); test_rc = 1; } } while (0)
;
128
129 free(argstr);
130 return pass;
131}
132
133/* -------------------------------------------------------------------------
134 * __add_hex_argument — append a hex-formatted integer argument
135 * -------------------------------------------------------------------------
136 */
137static bool_Bool test_add_hex_argument(void)
138{
139 char *argstr;
140 bool_Bool pass = true1, p;
141 int ret;
142
143 printf("\ntest___add_hex_argument:\n");
144
145 argstr = strdup("s");
146
147 ret = __add_hex_argument(&argstr, "v", -1, false0);
148 p = (ret == 0 && !strcmp(argstr, "s"));
149 CHECK(p, "arg<0 → no change")do { if (p) { printf(" " "arg<0 → no change" " " "[PASS]\n"
); } else { printf(" " "arg<0 → no change" " " "[FAIL]\n"
); test_rc = 1; } } while (0)
;
150 pass &= p;
151
152 ret = __add_hex_argument(&argstr, "v", 0, false0);
153 p = (ret == 0 && !strcmp(argstr, "s"));
154 CHECK(p, "arg=0 allow_zero=false → no change")do { if (p) { printf(" " "arg=0 allow_zero=false → no change"
" " "[PASS]\n"); } else { printf(" " "arg=0 allow_zero=false → no change"
" " "[FAIL]\n"); test_rc = 1; } } while (0)
;
155 pass &= p;
156
157 ret = __add_hex_argument(&argstr, "v", 0, true1);
158 p = (ret == 0 && !strcmp(argstr, "s,v=0x00000000"));
159 CHECK(p, "arg=0 allow_zero=true → \"%s\"", argstr)do { if (p) { printf(" " "arg=0 allow_zero=true → \"%s\"" " "
"[PASS]\n", argstr); } else { printf(" " "arg=0 allow_zero=true → \"%s\""
" " "[FAIL]\n", argstr); test_rc = 1; } } while (0)
;
160 pass &= p;
161
162 free(argstr);
163 argstr = strdup("s");
164 ret = __add_hex_argument(&argstr, "key", 0x1234abcd, false0);
165 p = (ret == 0 && !strcmp(argstr, "s,key=0x1234abcd"));
166 CHECK(p, "arg=0x1234abcd → \"%s\"", argstr)do { if (p) { printf(" " "arg=0x1234abcd → \"%s\"" " " "[PASS]\n"
, argstr); } else { printf(" " "arg=0x1234abcd → \"%s\"" " "
"[FAIL]\n", argstr); test_rc = 1; } } while (0)
;
167 pass &= p;
168
169 free(argstr);
170 return pass;
171}
172
173/* -------------------------------------------------------------------------
174 * __add_int_argument — append a decimal integer argument
175 * -------------------------------------------------------------------------
176 */
177static bool_Bool test_add_int_argument(void)
178{
179 char *argstr;
180 bool_Bool pass = true1, p;
181 int ret;
182
183 printf("\ntest___add_int_argument:\n");
184
185 argstr = strdup("s");
186
187 ret = __add_int_argument(&argstr, "n", -1, false0);
188 p = (ret == 0 && !strcmp(argstr, "s"));
189 CHECK(p, "arg<0 → no change")do { if (p) { printf(" " "arg<0 → no change" " " "[PASS]\n"
); } else { printf(" " "arg<0 → no change" " " "[FAIL]\n"
); test_rc = 1; } } while (0)
;
190 pass &= p;
191
192 ret = __add_int_argument(&argstr, "n", 0, false0);
193 p = (ret == 0 && !strcmp(argstr, "s"));
194 CHECK(p, "arg=0 allow_zero=false → no change")do { if (p) { printf(" " "arg=0 allow_zero=false → no change"
" " "[PASS]\n"); } else { printf(" " "arg=0 allow_zero=false → no change"
" " "[FAIL]\n"); test_rc = 1; } } while (0)
;
195 pass &= p;
196
197 ret = __add_int_argument(&argstr, "n", 0, true1);
198 p = (ret == 0 && !strcmp(argstr, "s,n=0"));
199 CHECK(p, "arg=0 allow_zero=true → \"%s\"", argstr)do { if (p) { printf(" " "arg=0 allow_zero=true → \"%s\"" " "
"[PASS]\n", argstr); } else { printf(" " "arg=0 allow_zero=true → \"%s\""
" " "[FAIL]\n", argstr); test_rc = 1; } } while (0)
;
200 pass &= p;
201
202 free(argstr);
203 argstr = strdup("s");
204 ret = __add_int_argument(&argstr, "n", 42, false0);
205 p = (ret == 0 && !strcmp(argstr, "s,n=42"));
206 CHECK(p, "arg=42 → \"%s\"", argstr)do { if (p) { printf(" " "arg=42 → \"%s\"" " " "[PASS]\n",
argstr); } else { printf(" " "arg=42 → \"%s\"" " " "[FAIL]\n"
, argstr); test_rc = 1; } } while (0)
;
207 pass &= p;
208
209 free(argstr);
210 return pass;
211}
212
213/* -------------------------------------------------------------------------
214 * __add_int_or_minus_one_argument — like _int but also allows -1
215 * -------------------------------------------------------------------------
216 */
217static bool_Bool test_add_int_or_minus_one_argument(void)
218{
219 char *argstr;
220 bool_Bool pass = true1, p;
221 int ret;
222
223 printf("\ntest___add_int_or_minus_one_argument:\n");
224
225 argstr = strdup("s");
226
227 ret = __add_int_or_minus_one_argument(&argstr, "n", -2);
228 p = (ret == 0 && !strcmp(argstr, "s"));
229 CHECK(p, "arg=-2 → no change")do { if (p) { printf(" " "arg=-2 → no change" " " "[PASS]\n"
); } else { printf(" " "arg=-2 → no change" " " "[FAIL]\n"
); test_rc = 1; } } while (0)
;
230 pass &= p;
231
232 ret = __add_int_or_minus_one_argument(&argstr, "n", -1);
233 p = (ret == 0 && !strcmp(argstr, "s,n=-1"));
234 CHECK(p, "arg=-1 → \"%s\"", argstr)do { if (p) { printf(" " "arg=-1 → \"%s\"" " " "[PASS]\n",
argstr); } else { printf(" " "arg=-1 → \"%s\"" " " "[FAIL]\n"
, argstr); test_rc = 1; } } while (0)
;
235 pass &= p;
236
237 free(argstr);
238 argstr = strdup("s");
239 ret = __add_int_or_minus_one_argument(&argstr, "n", 0);
240 p = (ret == 0 && !strcmp(argstr, "s,n=0"));
241 CHECK(p, "arg=0 → \"%s\"", argstr)do { if (p) { printf(" " "arg=0 → \"%s\"" " " "[PASS]\n", argstr
); } else { printf(" " "arg=0 → \"%s\"" " " "[FAIL]\n", argstr
); test_rc = 1; } } while (0)
;
242 pass &= p;
243
244 free(argstr);
245 argstr = strdup("s");
246 ret = __add_int_or_minus_one_argument(&argstr, "n", 7);
247 p = (ret == 0 && !strcmp(argstr, "s,n=7"));
248 CHECK(p, "arg=7 → \"%s\"", argstr)do { if (p) { printf(" " "arg=7 → \"%s\"" " " "[PASS]\n", argstr
); } else { printf(" " "arg=7 → \"%s\"" " " "[FAIL]\n", argstr
); test_rc = 1; } } while (0)
;
249 pass &= p;
250
251 free(argstr);
252 return pass;
253}
254
255/* -------------------------------------------------------------------------
256 * __add_argument — append a string argument; skip NULL/"none"/empty
257 * -------------------------------------------------------------------------
258 */
259static bool_Bool test_add_argument(void)
260{
261 char *argstr;
262 bool_Bool pass = true1, p;
263 int ret;
264
265 printf("\ntest___add_argument:\n");
266
267 argstr = strdup("s");
268
269 ret = __add_argument(&argstr, "a", NULL((void*)0));
270 p = (ret == 0 && !strcmp(argstr, "s"));
271 CHECK(p, "NULL → no change")do { if (p) { printf(" " "NULL → no change" " " "[PASS]\n"
); } else { printf(" " "NULL → no change" " " "[FAIL]\n");
test_rc = 1; } } while (0)
;
272 pass &= p;
273
274 ret = __add_argument(&argstr, "a", "");
275 p = (ret == 0 && !strcmp(argstr, "s"));
276 CHECK(p, "empty string → no change")do { if (p) { printf(" " "empty string → no change" " " "[PASS]\n"
); } else { printf(" " "empty string → no change" " " "[FAIL]\n"
); test_rc = 1; } } while (0)
;
277 pass &= p;
278
279 ret = __add_argument(&argstr, "a", "none");
280 p = (ret == 0 && !strcmp(argstr, "s"));
281 CHECK(p, "\"none\" → no change")do { if (p) { printf(" " "\"none\" → no change" " " "[PASS]\n"
); } else { printf(" " "\"none\" → no change" " " "[FAIL]\n"
); test_rc = 1; } } while (0)
;
282 pass &= p;
283
284 ret = __add_argument(&argstr, "transport", "tcp");
285 p = (ret == 0 && !strcmp(argstr, "s,transport=tcp"));
286 CHECK(p, "\"tcp\" → \"%s\"", argstr)do { if (p) { printf(" " "\"tcp\" → \"%s\"" " " "[PASS]\n"
, argstr); } else { printf(" " "\"tcp\" → \"%s\"" " " "[FAIL]\n"
, argstr); test_rc = 1; } } while (0)
;
287 pass &= p;
288
289 free(argstr);
290 return pass;
291}
292
293/* -------------------------------------------------------------------------
294 * inet4_pton — parse an IPv4 address string into a sockaddr_storage
295 * -------------------------------------------------------------------------
296 */
297static bool_Bool test_inet4_pton(void)
298{
299 struct sockaddr_storage addr;
300 struct sockaddr_in *a4 = (struct sockaddr_in *)&addr;
301 bool_Bool pass = true1, p;
302 int ret;
303
304 printf("\ntest_inet4_pton:\n");
305
306 memset(&addr, 0, sizeof(addr));
307 ret = inet4_pton("192.168.1.1", 4420, &addr);
308 p = (ret == 0 &&
309 a4->sin_family == AF_INET2 &&
310 ntohs(a4->sin_port)__bswap_16 (a4->sin_port) == 4420);
311 CHECK(p, "\"192.168.1.1\":4420 → ret=%d family=%d port=%d",do { if (p) { printf(" " "\"192.168.1.1\":4420 → ret=%d family=%d port=%d"
" " "[PASS]\n", ret, a4->sin_family, __bswap_16 (a4->sin_port
)); } else { printf(" " "\"192.168.1.1\":4420 → ret=%d family=%d port=%d"
" " "[FAIL]\n", ret, a4->sin_family, __bswap_16 (a4->sin_port
)); test_rc = 1; } } while (0)
312 ret, a4->sin_family, ntohs(a4->sin_port))do { if (p) { printf(" " "\"192.168.1.1\":4420 → ret=%d family=%d port=%d"
" " "[PASS]\n", ret, a4->sin_family, __bswap_16 (a4->sin_port
)); } else { printf(" " "\"192.168.1.1\":4420 → ret=%d family=%d port=%d"
" " "[FAIL]\n", ret, a4->sin_family, __bswap_16 (a4->sin_port
)); test_rc = 1; } } while (0)
;
313 pass &= p;
314
315 memset(&addr, 0, sizeof(addr));
316 ret = inet4_pton("0.0.0.0", 0, &addr);
317 p = (ret == 0 && a4->sin_family == AF_INET2);
318 CHECK(p, "\"0.0.0.0\":0 → ret=%d", ret)do { if (p) { printf(" " "\"0.0.0.0\":0 → ret=%d" " " "[PASS]\n"
, ret); } else { printf(" " "\"0.0.0.0\":0 → ret=%d" " " "[FAIL]\n"
, ret); test_rc = 1; } } while (0)
;
319 pass &= p;
320
321 ret = inet4_pton("not-an-ip", 4420, &addr);
322 p = (ret == -EINVAL22);
323 CHECK(p, "invalid string → -EINVAL (got %d)", ret)do { if (p) { printf(" " "invalid string → -EINVAL (got %d)"
" " "[PASS]\n", ret); } else { printf(" " "invalid string → -EINVAL (got %d)"
" " "[FAIL]\n", ret); test_rc = 1; } } while (0)
;
324 pass &= p;
325
326 ret = inet4_pton("999.999.999.999", 4420, &addr);
327 p = (ret == -EINVAL22);
328 CHECK(p, "out-of-range octets → -EINVAL (got %d)", ret)do { if (p) { printf(" " "out-of-range octets → -EINVAL (got %d)"
" " "[PASS]\n", ret); } else { printf(" " "out-of-range octets → -EINVAL (got %d)"
" " "[FAIL]\n", ret); test_rc = 1; } } while (0)
;
329 pass &= p;
330
331 /* string longer than INET_ADDRSTRLEN */
332 ret = inet4_pton(
333 "1234567890.1234567890.1234567890.1234567890",
334 0, &addr);
335 p = (ret == -EINVAL22);
336 CHECK(p, "too-long string → -EINVAL (got %d)", ret)do { if (p) { printf(" " "too-long string → -EINVAL (got %d)"
" " "[PASS]\n", ret); } else { printf(" " "too-long string → -EINVAL (got %d)"
" " "[FAIL]\n", ret); test_rc = 1; } } while (0)
;
337 pass &= p;
338
339 return pass;
340}
341
342/* -------------------------------------------------------------------------
343 * inet_pton_with_scope — parse IPv4 or IPv6 (with optional %iface scope)
344 * -------------------------------------------------------------------------
345 */
346static bool_Bool test_inet_pton_with_scope(struct libnvme_global_ctx *ctx)
347{
348 struct sockaddr_storage addr;
349 bool_Bool pass = true1, p;
350 int ret;
351
352 printf("\ntest_inet_pton_with_scope:\n");
353
354 /* IPv4 via AF_INET */
355 ret = inet_pton_with_scope(ctx, AF_INET2, "10.0.0.1", "4420", &addr);
356 p = (ret == 0);
357 CHECK(p, "AF_INET \"10.0.0.1\":4420 → ret=%d", ret)do { if (p) { printf(" " "AF_INET \"10.0.0.1\":4420 → ret=%d"
" " "[PASS]\n", ret); } else { printf(" " "AF_INET \"10.0.0.1\":4420 → ret=%d"
" " "[FAIL]\n", ret); test_rc = 1; } } while (0)
;
358 pass &= p;
359
360 /* IPv4 rejected when asking for AF_INET6 */
361 ret = inet_pton_with_scope(ctx, AF_INET610, "10.0.0.1", "4420", &addr);
362 p = (ret != 0);
363 CHECK(p, "AF_INET6 rejects IPv4 → ret=%d (non-zero expected)", ret)do { if (p) { printf(" " "AF_INET6 rejects IPv4 → ret=%d (non-zero expected)"
" " "[PASS]\n", ret); } else { printf(" " "AF_INET6 rejects IPv4 → ret=%d (non-zero expected)"
" " "[FAIL]\n", ret); test_rc = 1; } } while (0)
;
364 pass &= p;
365
366 /* Plain IPv6 via AF_INET6 */
367 ret = inet_pton_with_scope(ctx, AF_INET610, "2001:db8::1", "4420", &addr);
368 p = (ret == 0);
369 CHECK(p, "AF_INET6 \"2001:db8::1\" → ret=%d", ret)do { if (p) { printf(" " "AF_INET6 \"2001:db8::1\" → ret=%d"
" " "[PASS]\n", ret); } else { printf(" " "AF_INET6 \"2001:db8::1\" → ret=%d"
" " "[FAIL]\n", ret); test_rc = 1; } } while (0)
;
370 pass &= p;
371
372 /* Plain IPv6 via AF_UNSPEC */
373 ret = inet_pton_with_scope(ctx, AF_UNSPEC0, "fe80::1", "4420", &addr);
374 p = (ret == 0);
375 CHECK(p, "AF_UNSPEC \"fe80::1\" → ret=%d", ret)do { if (p) { printf(" " "AF_UNSPEC \"fe80::1\" → ret=%d" " "
"[PASS]\n", ret); } else { printf(" " "AF_UNSPEC \"fe80::1\" → ret=%d"
" " "[FAIL]\n", ret); test_rc = 1; } } while (0)
;
376 pass &= p;
377
378 /* Scoped link-local via lo (always present) */
379 ret = inet_pton_with_scope(ctx, AF_UNSPEC0, "fe80::1%lo", "4420", &addr);
380 p = (ret == 0);
381 CHECK(p, "AF_UNSPEC \"fe80::1%%lo\" (scoped): ret=%d", ret)do { if (p) { printf(" " "AF_UNSPEC \"fe80::1%%lo\" (scoped): ret=%d"
" " "[PASS]\n", ret); } else { printf(" " "AF_UNSPEC \"fe80::1%%lo\" (scoped): ret=%d"
" " "[FAIL]\n", ret); test_rc = 1; } } while (0)
;
382 pass &= p;
383
384 /* Scoped address for non-link-local: scope is ignored by inet6_pton */
385 ret = inet_pton_with_scope(ctx, AF_UNSPEC0, "2001:db8::1", NULL((void*)0), &addr);
386 p = (ret == 0);
387 CHECK(p, "AF_UNSPEC \"2001:db8::1\" trsvcid=NULL → ret=%d", ret)do { if (p) { printf(" " "AF_UNSPEC \"2001:db8::1\" trsvcid=NULL → ret=%d"
" " "[PASS]\n", ret); } else { printf(" " "AF_UNSPEC \"2001:db8::1\" trsvcid=NULL → ret=%d"
" " "[FAIL]\n", ret); test_rc = 1; } } while (0)
;
388 pass &= p;
389
390 /* Port overflow */
391 ret = inet_pton_with_scope(ctx, AF_INET2, "10.0.0.1", "99999", &addr);
392 p = (ret == -ERANGE34);
393 CHECK(p, "port overflow (99999) → -ERANGE (got %d)", ret)do { if (p) { printf(" " "port overflow (99999) → -ERANGE (got %d)"
" " "[PASS]\n", ret); } else { printf(" " "port overflow (99999) → -ERANGE (got %d)"
" " "[FAIL]\n", ret); test_rc = 1; } } while (0)
;
394 pass &= p;
395
396 return pass;
397}
398
399/* -------------------------------------------------------------------------
400 * traddr_is_hostname — decide whether traddr is a hostname (not an IP)
401 *
402 * This is the key regression test: scoped IPv6 addresses like
403 * "fe80::1%eth0" must NOT be classified as hostnames. Before this was
404 * caught, a proposed change replaced inet_pton_with_scope() with plain
405 * inet_pton(), which fails on scoped addresses and mis-classifies them
406 * as hostnames.
407 * -------------------------------------------------------------------------
408 */
409static bool_Bool test_traddr_is_hostname(struct libnvme_global_ctx *ctx)
410{
411 bool_Bool pass = true1, p;
412
413 printf("\ntest_traddr_is_hostname:\n");
414
415#define TRADDR_TEST(transport_, traddr_, expected, label) \
416 do { \
417 p = (traddr_is_hostname(ctx, transport_, \
418 traddr_) == (expected)); \
419 CHECK(p, "%-38s → %s", label, \do { if (p) { printf(" " "%-38s → %s" " " "[PASS]\n", label
, expected ? "hostname" : "IP"); } else { printf(" " "%-38s → %s"
" " "[FAIL]\n", label, expected ? "hostname" : "IP"); test_rc
= 1; } } while (0)
420 expected ? "hostname" : "IP")do { if (p) { printf(" " "%-38s → %s" " " "[PASS]\n", label
, expected ? "hostname" : "IP"); } else { printf(" " "%-38s → %s"
" " "[FAIL]\n", label, expected ? "hostname" : "IP"); test_rc
= 1; } } while (0)
; \
421 pass &= p; \
422 } while (0)
423
424 /* Plain IPv4 */
425 TRADDR_TEST("tcp", "192.168.1.10", false0, "IPv4 address (tcp)");
426 TRADDR_TEST("rdma", "192.168.1.10", false0, "IPv4 address (rdma)");
427
428 /* Plain IPv6 */
429 TRADDR_TEST("tcp", "fe80::1", false0, "IPv6 link-local (no scope)");
430 TRADDR_TEST("tcp", "2001:db8::1", false0, "IPv6 global unicast");
431 TRADDR_TEST("tcp", "::1", false0, "IPv6 loopback");
432
433 /*
434 * Scoped IPv6 - the regression case.
435 * Must be classified as an IP address, not a hostname.
436 * Plain inet_pton() would fail here and wrongly return true.
437 */
438 TRADDR_TEST("tcp", "fe80::1%lo", false0, "scoped IPv6 (lo, exists)");
439 TRADDR_TEST("rdma", "fe80::1%lo", false0, "scoped IPv6 (lo, rdma)");
440
441 /*
442 * A zone naming a nonexistent interface is still numeric; zone
443 * validity is the kernel's call, same as host_iface.
444 */
445 TRADDR_TEST("tcp", "fe80::1%nonexistent0", false0,
446 "scoped IPv6 (bad zone)");
447
448 /* Hostnames */
449 TRADDR_TEST("tcp", "storage.example.com", true1, "FQDN hostname");
450 TRADDR_TEST("tcp", "nvme-target", true1, "short hostname");
451 TRADDR_TEST("rdma", "storage.example.com",
452 true1, "FQDN hostname (rdma)");
453
454 /* "none" - reserved keyword, must not be treated as a hostname */
455 TRADDR_TEST("tcp", "none", false0, "literal \"none\"");
456
457 /* Transports where the check is skipped entirely */
458 TRADDR_TEST("pcie", "192.168.1.10", false0, "IPv4 on pcie");
459 TRADDR_TEST("fc", "fe80::1%lo", false0, "scoped IPv6 (fc)");
460 TRADDR_TEST("loop", "storage.example.com", false0, "hostname on loop");
461
462 /* NULL traddr */
463 TRADDR_TEST("tcp", NULL((void*)0), false0, "NULL traddr");
464
465#undef TRADDR_TEST
466
467 return pass;
468}
469
470/* -------------------------------------------------------------------------
471 * nvmf_sanitize_addrs — reject hostnames, canonicalize numeric addresses
472 * -------------------------------------------------------------------------
473 */
474static bool_Bool test_nvmf_sanitize_addrs(struct libnvme_global_ctx *ctx)
475{
476 struct libnvme_ctrl_params params = { .transport = "tcp" };
477 libnvme_ctrl_t c;
478 bool_Bool pass = true1, p;
479 int ret;
480
481 printf("\ntest_nvmf_sanitize_addrs:\n");
482
483 /*
484 * Creation never resolves and never rejects -- a hostname host_traddr
485 * is stored verbatim; rejection happens at connect (single policy
486 * point).
487 */
488 params.subsysnqn = "nqn.2024-01.com.example:test";
489 params.traddr = "192.168.1.10";
490 params.host_traddr = "storage.example.com";
491 ret = libnvme_create_ctrl(ctx, &params, &c);
1
Value assigned to 'c'
492 p = !ret && c && !strcmp(libnvme_ctrl_get_host_traddr(c),
2
Assuming 'ret' is 0
3
Assuming pointer value is null
4
Assuming 'c' is null
493 "storage.example.com");
494 CHECK(p, "hostname host_traddr stored verbatim at creation")do { if (p) { printf(" " "hostname host_traddr stored verbatim at creation"
" " "[PASS]\n"); } else { printf(" " "hostname host_traddr stored verbatim at creation"
" " "[FAIL]\n"); test_rc = 1; } } while (0)
;
5
Taking false branch
6
Loop condition is false. Exiting loop
495 pass &= p;
496
497 ret = nvmf_sanitize_addrs(ctx, c);
7
Passing null pointer value via 2nd parameter 'c'
8
Calling 'nvmf_sanitize_addrs'
498 p = ret == -ENVME_CONNECT_TRADDR;
499 CHECK(p, "hostname host_traddr rejected at connect: %d", ret)do { if (p) { printf(" " "hostname host_traddr rejected at connect: %d"
" " "[PASS]\n", ret); } else { printf(" " "hostname host_traddr rejected at connect: %d"
" " "[FAIL]\n", ret); test_rc = 1; } } while (0)
;
500 pass &= p;
501
502 libnvme_free_ctrl(c);
503
504 /* A hostname traddr is rejected the same way. */
505 params.traddr = "storage.example.com";
506 params.host_traddr = NULL((void*)0);
507 ret = libnvme_create_ctrl(ctx, &params, &c);
508 p = !ret && c != NULL((void*)0);
509 CHECK(p, "ctrl created with hostname traddr")do { if (p) { printf(" " "ctrl created with hostname traddr"
" " "[PASS]\n"); } else { printf(" " "ctrl created with hostname traddr"
" " "[FAIL]\n"); test_rc = 1; } } while (0)
;
510 pass &= p;
511
512 ret = nvmf_sanitize_addrs(ctx, c);
513 p = ret == -ENVME_CONNECT_TRADDR;
514 CHECK(p, "hostname traddr rejected at connect: %d", ret)do { if (p) { printf(" " "hostname traddr rejected at connect: %d"
" " "[PASS]\n", ret); } else { printf(" " "hostname traddr rejected at connect: %d"
" " "[FAIL]\n", ret); test_rc = 1; } } while (0)
;
515 pass &= p;
516
517 libnvme_free_ctrl(c);
518
519 /* An uncompressed IPv6 traddr is canonicalized. */
520 params.traddr = "2001:0db8:0000:0000:0000:0000:0000:0001";
521 ret = libnvme_create_ctrl(ctx, &params, &c);
522 p = !ret && c != NULL((void*)0);
523 CHECK(p, "ctrl created with uncompressed IPv6 traddr")do { if (p) { printf(" " "ctrl created with uncompressed IPv6 traddr"
" " "[PASS]\n"); } else { printf(" " "ctrl created with uncompressed IPv6 traddr"
" " "[FAIL]\n"); test_rc = 1; } } while (0)
;
524 pass &= p;
525
526 ret = nvmf_sanitize_addrs(ctx, c);
527 p = !ret && !strcmp(libnvme_ctrl_get_traddr(c), "2001:db8::1");
528 CHECK(p, "uncompressed IPv6 traddr canonicalized: %s",do { if (p) { printf(" " "uncompressed IPv6 traddr canonicalized: %s"
" " "[PASS]\n", libnvme_ctrl_get_traddr(c)); } else { printf
(" " "uncompressed IPv6 traddr canonicalized: %s" " " "[FAIL]\n"
, libnvme_ctrl_get_traddr(c)); test_rc = 1; } } while (0)
529 libnvme_ctrl_get_traddr(c))do { if (p) { printf(" " "uncompressed IPv6 traddr canonicalized: %s"
" " "[PASS]\n", libnvme_ctrl_get_traddr(c)); } else { printf
(" " "uncompressed IPv6 traddr canonicalized: %s" " " "[FAIL]\n"
, libnvme_ctrl_get_traddr(c)); test_rc = 1; } } while (0)
;
530 pass &= p;
531
532 libnvme_free_ctrl(c);
533
534 /*
535 * A scoped IPv6 traddr passes through verbatim even when the zone
536 * names a nonexistent interface -- zone validity is the kernel's
537 * call, same as host_iface.
538 */
539 params.traddr = "fe80::1%nonexistent0";
540 ret = libnvme_create_ctrl(ctx, &params, &c);
541 p = !ret && c != NULL((void*)0);
542 CHECK(p, "ctrl created with bad-zone scoped IPv6 traddr")do { if (p) { printf(" " "ctrl created with bad-zone scoped IPv6 traddr"
" " "[PASS]\n"); } else { printf(" " "ctrl created with bad-zone scoped IPv6 traddr"
" " "[FAIL]\n"); test_rc = 1; } } while (0)
;
543 pass &= p;
544
545 ret = nvmf_sanitize_addrs(ctx, c);
546 p = !ret && !strcmp(libnvme_ctrl_get_traddr(c), "fe80::1%nonexistent0");
547 CHECK(p, "bad-zone scoped IPv6 traddr accepted verbatim: %s",do { if (p) { printf(" " "bad-zone scoped IPv6 traddr accepted verbatim: %s"
" " "[PASS]\n", libnvme_ctrl_get_traddr(c)); } else { printf
(" " "bad-zone scoped IPv6 traddr accepted verbatim: %s" " "
"[FAIL]\n", libnvme_ctrl_get_traddr(c)); test_rc = 1; } } while
(0)
548 libnvme_ctrl_get_traddr(c))do { if (p) { printf(" " "bad-zone scoped IPv6 traddr accepted verbatim: %s"
" " "[PASS]\n", libnvme_ctrl_get_traddr(c)); } else { printf
(" " "bad-zone scoped IPv6 traddr accepted verbatim: %s" " "
"[FAIL]\n", libnvme_ctrl_get_traddr(c)); test_rc = 1; } } while
(0)
;
549 pass &= p;
550
551 libnvme_free_ctrl(c);
552
553 /* fc: a WWN traddr is left untouched (not an IP transport). */
554 params.transport = "fc";
555 params.traddr = "nn-0x1:pn-0x2";
556 ret = libnvme_create_ctrl(ctx, &params, &c);
557 p = !ret && c != NULL((void*)0);
558 CHECK(p, "ctrl created with fc WWN traddr")do { if (p) { printf(" " "ctrl created with fc WWN traddr" " "
"[PASS]\n"); } else { printf(" " "ctrl created with fc WWN traddr"
" " "[FAIL]\n"); test_rc = 1; } } while (0)
;
559 pass &= p;
560
561 ret = nvmf_sanitize_addrs(ctx, c);
562 p = !ret && !strcmp(libnvme_ctrl_get_traddr(c), "nn-0x1:pn-0x2");
563 CHECK(p, "fc WWN traddr left untouched: %s",do { if (p) { printf(" " "fc WWN traddr left untouched: %s" " "
"[PASS]\n", libnvme_ctrl_get_traddr(c)); } else { printf(" "
"fc WWN traddr left untouched: %s" " " "[FAIL]\n", libnvme_ctrl_get_traddr
(c)); test_rc = 1; } } while (0)
564 libnvme_ctrl_get_traddr(c))do { if (p) { printf(" " "fc WWN traddr left untouched: %s" " "
"[PASS]\n", libnvme_ctrl_get_traddr(c)); } else { printf(" "
"fc WWN traddr left untouched: %s" " " "[FAIL]\n", libnvme_ctrl_get_traddr
(c)); test_rc = 1; } } while (0)
;
565 pass &= p;
566
567 libnvme_free_ctrl(c);
568
569 /* loop: no traddr at all -- nothing to sanitize. */
570 params.transport = "loop";
571 params.traddr = NULL((void*)0);
572 ret = libnvme_create_ctrl(ctx, &params, &c);
573 p = !ret && c != NULL((void*)0);
574 CHECK(p, "ctrl created for loop transport")do { if (p) { printf(" " "ctrl created for loop transport" " "
"[PASS]\n"); } else { printf(" " "ctrl created for loop transport"
" " "[FAIL]\n"); test_rc = 1; } } while (0)
;
575 pass &= p;
576
577 ret = nvmf_sanitize_addrs(ctx, c);
578 p = ret == 0;
579 CHECK(p, "loop transport (no traddr) sanitizes cleanly: %d", ret)do { if (p) { printf(" " "loop transport (no traddr) sanitizes cleanly: %d"
" " "[PASS]\n", ret); } else { printf(" " "loop transport (no traddr) sanitizes cleanly: %d"
" " "[FAIL]\n", ret); test_rc = 1; } } while (0)
;
580 pass &= p;
581
582 libnvme_free_ctrl(c);
583
584 return pass;
585}
586
587/* -------------------------------------------------------------------------
588 * unescape_uri — decode percent-encoded characters in a URI fragment
589 * -------------------------------------------------------------------------
590 */
591static bool_Bool test_unescape_uri(void)
592{
593 char *out;
594 bool_Bool pass = true1, p;
595
596 printf("\ntest_unescape_uri:\n");
597
598 out = unescape_uri("hello%20world", -1);
599 p = out && !strcmp(out, "hello world");
600 CHECK(p, "%%20 → space: \"%s\"", out ? out : "(null)")do { if (p) { printf(" " "%%20 → space: \"%s\"" " " "[PASS]\n"
, out ? out : "(null)"); } else { printf(" " "%%20 → space: \"%s\""
" " "[FAIL]\n", out ? out : "(null)"); test_rc = 1; } } while
(0)
;
601 pass &= p;
602 free(out);
603
604 out = unescape_uri("path%2Fsegment", -1);
605 p = out && !strcmp(out, "path/segment");
606 CHECK(p, "%%2F → /: \"%s\"", out ? out : "(null)")do { if (p) { printf(" " "%%2F → /: \"%s\"" " " "[PASS]\n"
, out ? out : "(null)"); } else { printf(" " "%%2F → /: \"%s\""
" " "[FAIL]\n", out ? out : "(null)"); test_rc = 1; } } while
(0)
;
607 pass &= p;
608 free(out);
609
610 out = unescape_uri("no-escapes", -1);
611 p = out && !strcmp(out, "no-escapes");
612 CHECK(p, "no escapes → unchanged: \"%s\"", out ? out : "(null)")do { if (p) { printf(" " "no escapes → unchanged: \"%s\"" " "
"[PASS]\n", out ? out : "(null)"); } else { printf(" " "no escapes → unchanged: \"%s\""
" " "[FAIL]\n", out ? out : "(null)"); test_rc = 1; } } while
(0)
;
613 pass &= p;
614 free(out);
615
616 /* Truncate via explicit length */
617 out = unescape_uri("hello%20world", 5);
618 p = out && !strcmp(out, "hello");
619 CHECK(p, "len=5 → \"%s\"", out ? out : "(null)")do { if (p) { printf(" " "len=5 → \"%s\"" " " "[PASS]\n", out
? out : "(null)"); } else { printf(" " "len=5 → \"%s\"" " "
"[FAIL]\n", out ? out : "(null)"); test_rc = 1; } } while (0
)
;
620 pass &= p;
621 free(out);
622
623 /* Invalid percent sequence — passed through verbatim */
624 out = unescape_uri("bad%xychars", -1);
625 p = out && !strcmp(out, "bad%xychars");
626 CHECK(p, "invalid %%xy → verbatim: \"%s\"", out ? out : "(null)")do { if (p) { printf(" " "invalid %%xy → verbatim: \"%s\""
" " "[PASS]\n", out ? out : "(null)"); } else { printf(" " "invalid %%xy → verbatim: \"%s\""
" " "[FAIL]\n", out ? out : "(null)"); test_rc = 1; } } while
(0)
;
627 pass &= p;
628 free(out);
629
630 /* Truncated percent at end of string */
631 out = unescape_uri("end%2", -1);
632 p = out && !strcmp(out, "end%2");
633 CHECK(p, "truncated %% at end -> verbatim: \"%s\"",do { if (p) { printf(" " "truncated %% at end -> verbatim: \"%s\""
" " "[PASS]\n", out ? out : "(null)"); } else { printf(" " "truncated %% at end -> verbatim: \"%s\""
" " "[FAIL]\n", out ? out : "(null)"); test_rc = 1; } } while
(0)
634 out ? out : "(null)")do { if (p) { printf(" " "truncated %% at end -> verbatim: \"%s\""
" " "[PASS]\n", out ? out : "(null)"); } else { printf(" " "truncated %% at end -> verbatim: \"%s\""
" " "[FAIL]\n", out ? out : "(null)"); test_rc = 1; } } while
(0)
;
635 pass &= p;
636 free(out);
637
638 return pass;
639}
640
641/* -------------------------------------------------------------------------
642 * main
643 * -------------------------------------------------------------------------
644 */
645int main(int argc, char *argv[])
646{
647 struct libnvme_global_ctx *ctx;
648
649 test_rc = EXIT_SUCCESS0;
650
651 /*
652 * A real context is needed for functions that call libnvme_msg()
653 * on error paths (inet6_pton, traddr_is_hostname). Use LOG_ERR
654 * so test output stays clean during normal runs.
655 */
656 ctx = libnvme_create_global_ctx();
657 if (!ctx) {
658 fprintf(stderrstderr, "failed to create libnvme context\n");
659 return EXIT_FAILURE1;
660 }
661 libnvme_set_logging_level(ctx, LIBNVME_LOG_ERR, false0, false0);
662
663 test_strchomp();
664 test_hostid_from_hostnqn();
665 test_add_bool_argument();
666 test_add_hex_argument();
667 test_add_int_argument();
668 test_add_int_or_minus_one_argument();
669 test_add_argument();
670 test_inet4_pton();
671 test_inet_pton_with_scope(ctx);
672 test_traddr_is_hostname(ctx);
673 test_nvmf_sanitize_addrs(ctx);
674 test_unescape_uri();
675
676 libnvme_free_global_ctx(ctx);
677
678 if (test_rc == EXIT_SUCCESS0)
679 printf("\nAll tests passed.\n");
680 else
681 printf("\nSOME TESTS FAILED.\n");
682 return test_rc;
683}

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