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