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
identify_controller(struct nvmf_qpair * qp)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 *
nvmf_address_family(uint8_t adrfam)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 *
nvmf_subsystem_type(uint8_t subtype)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 *
nvmf_secure_channel(uint8_t treq)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 *
nvmf_controller_id(uint16_t cntlid)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 *
nvmf_rdma_service_type(uint8_t qptype)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 *
nvmf_rdma_provider_type(uint8_t prtype)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 *
nvmf_rdma_cms(uint8_t cms)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 *
nvmf_tcp_security_type(uint8_t sectype)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
print_discovery_entry(u_int i,struct nvme_discovery_log_entry * entry)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
dump_discovery_log_page(struct nvmf_qpair * qp)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
discover(const struct cmd * f,int argc,char * argv[])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