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
19static 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
31static void
32identify_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
43static const char *
44nvmf_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
65static const char *
66nvmf_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
81static const char *
82nvmf_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
96static const char *
97nvmf_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
112static const char *
113nvmf_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
128static const char *
129nvmf_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
150static const char *
151nvmf_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
164static const char *
165nvmf_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
182static void
183print_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
221static void
222dump_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
242static void
243discover(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
274static 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
286static const struct args discover_args[] = {
287	{ arg_string, &opt.address, "address" },
288	{ arg_none, NULL, NULL },
289};
290
291static 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
300CMD_COMMAND(discover_cmd);
301