xref: /freebsd/sbin/nvmecontrol/discover.c (revision b985c9ca)
1 /*-
2  * SPDX-License-Identifier: BSD-2-Clause
3  *
4  * Copyright (c) 2023-2024 Chelsio Communications, Inc.
5  * Written by: John Baldwin <jhb@FreeBSD.org>
6  */
7 
8 #include <err.h>
9 #include <libnvmf.h>
10 #include <stdio.h>
11 #include <stdlib.h>
12 #include <string.h>
13 #include <sysexits.h>
14 
15 #include "comnd.h"
16 #include "fabrics.h"
17 #include "nvmecontrol_ext.h"
18 
19 static struct options {
20 	const char	*transport;
21 	const char	*address;
22 	const char	*hostnqn;
23 	bool		verbose;
24 } opt = {
25 	.transport = "tcp",
26 	.address = NULL,
27 	.hostnqn = NULL,
28 	.verbose = false,
29 };
30 
31 static void
32 identify_controller(struct nvmf_qpair *qp)
33 {
34 	struct nvme_controller_data cdata;
35 	int error;
36 
37 	error = nvmf_host_identify_controller(qp, &cdata);
38 	if (error != 0)
39 		errc(EX_IOERR, error, "Failed to fetch controller data");
40 	nvme_print_controller(&cdata);
41 }
42 
43 static const char *
44 nvmf_address_family(uint8_t adrfam)
45 {
46 	static char buf[8];
47 
48 	switch (adrfam) {
49 	case NVMF_ADRFAM_IPV4:
50 		return ("AF_INET");
51 	case NVMF_ADRFAM_IPV6:
52 		return ("AF_INET6");
53 	case NVMF_ADRFAM_IB:
54 		return ("InfiniBand");
55 	case NVMF_ADRFAM_FC:
56 		return ("Fibre Channel");
57 	case NVMF_ADRFAM_INTRA_HOST:
58 		return ("Intra-host");
59 	default:
60 		snprintf(buf, sizeof(buf), "0x%02x\n", adrfam);
61 		return (buf);
62 	}
63 }
64 
65 static const char *
66 nvmf_subsystem_type(uint8_t subtype)
67 {
68 	static char buf[8];
69 
70 	switch (subtype) {
71 	case NVMF_SUBTYPE_DISCOVERY:
72 		return ("Discovery");
73 	case NVMF_SUBTYPE_NVME:
74 		return ("NVMe");
75 	default:
76 		snprintf(buf, sizeof(buf), "0x%02x\n", subtype);
77 		return (buf);
78 	}
79 }
80 
81 static const char *
82 nvmf_secure_channel(uint8_t treq)
83 {
84 	switch (treq & 0x03) {
85 	case NVMF_TREQ_SECURE_CHANNEL_NOT_SPECIFIED:
86 		return ("Not specified");
87 	case NVMF_TREQ_SECURE_CHANNEL_REQUIRED:
88 		return ("Required");
89 	case NVMF_TREQ_SECURE_CHANNEL_NOT_REQUIRED:
90 		return ("Not required");
91 	default:
92 		return ("0x03");
93 	}
94 }
95 
96 static const char *
97 nvmf_controller_id(uint16_t cntlid)
98 {
99 	static char buf[8];
100 
101 	switch (cntlid) {
102 	case NVMF_CNTLID_DYNAMIC:
103 		return ("Dynamic");
104 	case NVMF_CNTLID_STATIC_ANY:
105 		return ("Static");
106 	default:
107 		snprintf(buf, sizeof(buf), "%u", cntlid);
108 		return (buf);
109 	}
110 }
111 
112 static const char *
113 nvmf_rdma_service_type(uint8_t qptype)
114 {
115 	static char buf[8];
116 
117 	switch (qptype) {
118 	case NVMF_RDMA_QPTYPE_RELIABLE_CONNECTED:
119 		return ("Reliable connected");
120 	case NVMF_RDMA_QPTYPE_RELIABLE_DATAGRAM:
121 		return ("Reliable datagram");
122 	default:
123 		snprintf(buf, sizeof(buf), "0x%02x\n", qptype);
124 		return (buf);
125 	}
126 }
127 
128 static const char *
129 nvmf_rdma_provider_type(uint8_t prtype)
130 {
131 	static char buf[8];
132 
133 	switch (prtype) {
134 	case NVMF_RDMA_PRTYPE_NONE:
135 		return ("None");
136 	case NVMF_RDMA_PRTYPE_IB:
137 		return ("InfiniBand");
138 	case NVMF_RDMA_PRTYPE_ROCE:
139 		return ("RoCE (v1)");
140 	case NVMF_RDMA_PRTYPE_ROCE2:
141 		return ("RoCE (v2)");
142 	case NVMF_RDMA_PRTYPE_IWARP:
143 		return ("iWARP");
144 	default:
145 		snprintf(buf, sizeof(buf), "0x%02x\n", prtype);
146 		return (buf);
147 	}
148 }
149 
150 static const char *
151 nvmf_rdma_cms(uint8_t cms)
152 {
153 	static char buf[8];
154 
155 	switch (cms) {
156 	case NVMF_RDMA_CMS_RDMA_CM:
157 		return ("RDMA_IP_CM");
158 	default:
159 		snprintf(buf, sizeof(buf), "0x%02x\n", cms);
160 		return (buf);
161 	}
162 }
163 
164 static const char *
165 nvmf_tcp_security_type(uint8_t sectype)
166 {
167 	static char buf[8];
168 
169 	switch (sectype) {
170 	case NVME_TCP_SECURITY_NONE:
171 		return ("None");
172 	case NVME_TCP_SECURITY_TLS_1_2:
173 		return ("TLS 1.2");
174 	case NVME_TCP_SECURITY_TLS_1_3:
175 		return ("TLS 1.3");
176 	default:
177 		snprintf(buf, sizeof(buf), "0x%02x\n", sectype);
178 		return (buf);
179 	}
180 }
181 
182 static void
183 print_discovery_entry(u_int i, struct nvme_discovery_log_entry *entry)
184 {
185 	printf("Entry %02d\n", i + 1);
186 	printf("========\n");
187 	printf(" Transport type:       %s\n",
188 	    nvmf_transport_type(entry->trtype));
189 	printf(" Address family:       %s\n",
190 	    nvmf_address_family(entry->adrfam));
191 	printf(" Subsystem type:       %s\n",
192 	    nvmf_subsystem_type(entry->subtype));
193 	printf(" SQ flow control:      %s\n",
194 	    (entry->treq & (1 << 2)) == 0 ? "required" : "optional");
195 	printf(" Secure Channel:       %s\n", nvmf_secure_channel(entry->treq));
196 	printf(" Port ID:              %u\n", entry->portid);
197 	printf(" Controller ID:        %s\n",
198 	    nvmf_controller_id(entry->cntlid));
199 	printf(" Max Admin SQ Size:    %u\n", entry->aqsz);
200 	printf(" Sub NQN:              %s\n", entry->subnqn);
201 	printf(" Transport address:    %s\n", entry->traddr);
202 	printf(" Service identifier:   %s\n", entry->trsvcid);
203 	switch (entry->trtype) {
204 	case NVMF_TRTYPE_RDMA:
205 		printf(" RDMA Service Type:    %s\n",
206 		    nvmf_rdma_service_type(entry->tsas.rdma.rdma_qptype));
207 		printf(" RDMA Provider Type:   %s\n",
208 		    nvmf_rdma_provider_type(entry->tsas.rdma.rdma_prtype));
209 		printf(" RDMA CMS:             %s\n",
210 		    nvmf_rdma_cms(entry->tsas.rdma.rdma_cms));
211 		printf(" Partition key:        %u\n",
212 		    entry->tsas.rdma.rdma_pkey);
213 		break;
214 	case NVMF_TRTYPE_TCP:
215 		printf(" Security Type:        %s\n",
216 		    nvmf_tcp_security_type(entry->tsas.tcp.sectype));
217 		break;
218 	}
219 }
220 
221 static void
222 dump_discovery_log_page(struct nvmf_qpair *qp)
223 {
224 	struct nvme_discovery_log *log;
225 	int error;
226 
227 	error = nvmf_host_fetch_discovery_log_page(qp, &log);
228 	if (error != 0)
229 		errc(EX_IOERR, error, "Failed to fetch discovery log page");
230 
231 	printf("Discovery\n");
232 	printf("=========\n");
233 	if (log->numrec == 0) {
234 		printf("No entries found\n");
235 	} else {
236 		for (u_int i = 0; i < log->numrec; i++)
237 			print_discovery_entry(i, &log->entries[i]);
238 	}
239 	free(log);
240 }
241 
242 static void
243 discover(const struct cmd *f, int argc, char *argv[])
244 {
245 	enum nvmf_trtype trtype;
246 	struct nvmf_qpair *qp;
247 	const char *address, *port;
248 	char *tofree;
249 
250 	if (arg_parse(argc, argv, f))
251 		return;
252 
253 	if (strcasecmp(opt.transport, "tcp") == 0) {
254 		trtype = NVMF_TRTYPE_TCP;
255 	} else
256 		errx(EX_USAGE, "Unsupported or invalid transport");
257 
258 	nvmf_parse_address(opt.address, &address, &port, &tofree);
259 	qp = connect_discovery_adminq(trtype, address, port, opt.hostnqn);
260 	free(tofree);
261 
262 	/* Use Identify to fetch controller data */
263 	if (opt.verbose) {
264 		identify_controller(qp);
265 		printf("\n");
266 	}
267 
268 	/* Fetch Log pages */
269 	dump_discovery_log_page(qp);
270 
271 	nvmf_free_qpair(qp);
272 }
273 
274 static const struct opts discover_opts[] = {
275 #define OPT(l, s, t, opt, addr, desc) { l, s, t, &opt.addr, desc }
276 	OPT("transport", 't', arg_string, opt, transport,
277 	    "Transport type"),
278 	OPT("hostnqn", 'q', arg_string, opt, hostnqn,
279 	    "Host NQN"),
280 	OPT("verbose", 'v', arg_none, opt, verbose,
281 	    "Display the discovery controller's controller data"),
282 	{ NULL, 0, arg_none, NULL, NULL }
283 };
284 #undef OPT
285 
286 static const struct args discover_args[] = {
287 	{ arg_string, &opt.address, "address" },
288 	{ arg_none, NULL, NULL },
289 };
290 
291 static struct cmd discover_cmd = {
292 	.name = "discover",
293 	.fn = discover,
294 	.descr = "List discovery log pages from a fabrics controller",
295 	.ctx_size = sizeof(opt),
296 	.opts = discover_opts,
297 	.args = discover_args,
298 };
299 
300 CMD_COMMAND(discover_cmd);
301