/* * Copyright(c) 2009 Intel Corporation. All rights reserved. * * This program is free software; you can redistribute it and/or modify it * under the terms and conditions of the GNU General Public License, * version 2, as published by the Free Software Foundation. * * This program is distributed in the hope it will be useful, but WITHOUT * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for * more details. * * You should have received a copy of the GNU General Public License along with * this program; if not, write to the Free Software Foundation, Inc., * 51 Franklin St - Fifth Floor, Boston, MA 02110-1301 USA. * * Maintained at www.Open-FCoE.org */ #define _GNU_SOURCE #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include typedef __u8 u8; typedef __u16 u16; typedef __u32 u32; typedef __u64 u64; #include #include #include "fc_gs.h" #include "fc_ns.h" #include "scsi_bsg_fc.h" static bool quiet = false; __attribute__((__format__(__printf__, 2, 3))) static int print_result(const char *prefix, const char *format, ...) { va_list ap; int rc; va_start(ap, format); if (!quiet) printf("%s: ", prefix); rc = vprintf(format, ap); va_end(ap); return rc; } __attribute__((__format__(__printf__, 1, 2))) static int print_err(const char *format, ...) { va_list ap; int rc; if (quiet) return 0; va_start(ap, format); rc = vprintf(format, ap); va_end(ap); return rc; } #define ntoh24(n) (u32) ((n)[0] << 16 | (n)[1] << 8 | (n)[2]) #define hton24(h) { (h) >> 16 & 0xff, (h) >> 8 & 0xff, (h) & 0xff } static u64 ntohll(u64 netll) { u8 *_netll = (u8 *)&netll; return (u64) _netll[0] << (7 * 8) | (u64) _netll[1] << (6 * 8) | (u64) _netll[2] << (5 * 8) | (u64) _netll[3] << (4 * 8) | (u64) _netll[4] << (3 * 8) | (u64) _netll[5] << (2 * 8) | (u64) _netll[6] << (1 * 8) | (u64) _netll[7]; } static u64 htonll(u64 hostll) { u64 netll; u8 *_netll = (u8 *)&netll; _netll[0] = hostll >> (7 * 8) & 0xff; _netll[1] = hostll >> (6 * 8) & 0xff; _netll[2] = hostll >> (5 * 8) & 0xff; _netll[3] = hostll >> (4 * 8) & 0xff; _netll[4] = hostll >> (3 * 8) & 0xff; _netll[5] = hostll >> (2 * 8) & 0xff; _netll[6] = hostll >> (1 * 8) & 0xff; _netll[7] = hostll & 0xff; return netll; } static char* rjt_reason[] = { [1] = "Invalid command code", [2] = "Invalid version level", [3] = "Logical error", [4] = "Invalid CT_IU size", [5] = "Logical busy", [7] = "Protocol error", [9] = "Unable to perform command request", [0xB] = "Command not supported", [0xD] = "Server not available", [0xE] = "Session could not be established", }; static char* rjt_explan[] = { [0] = "No additional explanation", [1] = "Port Identifier not registered", [2] = "Port Name not registered", [3] = "Node Name not registered", [4] = "Class of Service not registered", [6] = "Initial Process Associator not registered", [7] = "FC-4 Types not registered", [8] = "Symbolic Port Name not registered", [9] = "Symbolic Node Name not registered", [0xA] = "Port Type not registered", [0xC] = "Fabric Port Name not registered", [0xD] = "Hard Address not registered", [0xF] = "FC-4 Features not registered", [0x10] = "Access denied", [0x11] = "Unacceptable Port Identifier", [0x12] = "Data base empty", [0x13] = "No object registered in the specified scope", [0x14] = "Domain ID not present", [0x15] = "Port number not present", [0x16] = "No device attached", [0xf0] = "Authorization Exception", [0xf1] = "Authentication Exception", [0xf2] = "Data base full", [0xf3] = "Data base empty", [0xf4] = "Processing request", [0xf5] = "Unable to verify connection", [0xf6] = "Devices not in a common zone", }; static u16 ct_rjt(u8 reason, u8 explan) { return (u16) reason << 8 | explan; } static u8 ct_rjt_reason(u16 rjt) { return (u8)(rjt >> 8); } static u8 ct_rjt_explan(u16 rjt) { return (u8) rjt & 0xff; } static int ns_query(int bsg, void *req, int req_len, void *resp, int resp_len) { char sense[96]; struct fc_bsg_request cdb = { .msgcode = FC_BSG_HST_CT, .rqst_data.h_ct = { .port_id = hton24(0xfffffc), } }; struct sg_io_v4 sgio = { .guard = 'Q', .protocol = BSG_PROTOCOL_SCSI, .subprotocol = BSG_SUB_PROTOCOL_SCSI_TRANSPORT, .request_len = sizeof(cdb), .request = (uintptr_t) &cdb, .dout_xfer_len = req_len, .dout_xferp = (uintptr_t) req, .din_xfer_len = resp_len, .din_xferp = (uintptr_t) resp, .max_response_len = sizeof(sense), .response = (uintptr_t) &sense, .timeout = 1000, }; return ioctl(bsg, SG_IO, &sgio); } static u16 gn_id(int bsg, u32 fcid, u16 cmd, u64 *wwn) { struct { struct fc_ct_hdr hdr; u64 wwn; } __attribute__((__packed__)) gn_resp; struct { struct fc_ct_hdr hdr; u8 resv; u8 port_id[3]; } __attribute__((__packed__)) gn = { .hdr = { .ct_rev = FC_CT_REV, .ct_fs_type = FC_FST_DIR, .ct_fs_subtype = FC_NS_SUBTYPE, .ct_cmd = htons(cmd), .ct_mr_size = htons(sizeof(gn_resp)), .ct_vendor = 0, }, .port_id = hton24(fcid), }; if (ns_query(bsg, &gn, sizeof(gn), &gn_resp, sizeof(gn_resp)) < 0) return ~0; if (gn_resp.hdr.ct_cmd != htons(FC_FS_ACC)) return ct_rjt(gn_resp.hdr.ct_reason, gn_resp.hdr.ct_explan); *wwn = ntohll(gn_resp.wwn); return 0; } #define FC_NS_GPN_ID 0x0112 static int gpn_id(int bsg, u32 fcid) { u64 wwpn; u16 rjt; rjt = gn_id(bsg, fcid, FC_NS_GPN_ID, &wwpn); if (rjt) goto fail; print_result("Port Name", "%16.16jx\n", (uintmax_t)wwpn); return 0; fail: if (rjt == (u16) ~0) print_err("%s ioctl failed: %s\n", __func__, strerror(errno)); else print_err("%s command failed: %s, %s\n", __func__, rjt_reason[ct_rjt_reason(rjt)], rjt_explan[ct_rjt_explan(rjt)]); return rjt; } #define FC_NS_GNN_ID 0x0113 static int gnn_id(int bsg, u32 fcid) { u64 wwnn; u16 rjt; rjt = gn_id(bsg, fcid, FC_NS_GNN_ID, &wwnn); if (rjt) goto fail; print_result("Node Name", "%16.16jx\n", (uintmax_t)wwnn); return 0; fail: if (rjt == (u16) ~0) print_err("%s ioctl failed: %s\n", __func__, strerror(errno)); else print_err("%s command failed: %s, %s\n", __func__, rjt_reason[ct_rjt_reason(rjt)], rjt_explan[ct_rjt_explan(rjt)]); return rjt; } #define FC_NS_GSPN_ID 0x0118 static int gspn_id(int bsg, u32 fcid) { struct { struct fc_ct_hdr hdr; u8 len; char name[255]; } __attribute__((__packed__)) gspn_resp; struct { struct fc_ct_hdr hdr; u8 resv; u8 port_id[3]; } __attribute__((__packed__)) gspn = { .hdr = { .ct_rev = FC_CT_REV, .ct_fs_type = FC_FST_DIR, .ct_fs_subtype = FC_NS_SUBTYPE, .ct_cmd = htons(FC_NS_GSPN_ID), .ct_mr_size = htons(sizeof(gspn_resp)), .ct_vendor = 0, }, .port_id = hton24(fcid), }; if (ns_query(bsg, &gspn, sizeof(gspn), &gspn_resp, sizeof(gspn_resp)) < 0) { print_err("%s ioctl failed: %s\n", __func__, strerror(errno)); return ~0; } if (gspn_resp.hdr.ct_cmd != htons(FC_FS_ACC)) { print_err("%s command failed: %s, %s\n", __func__, rjt_reason[gspn_resp.hdr.ct_reason], rjt_explan[gspn_resp.hdr.ct_explan]); return ct_rjt(gspn_resp.hdr.ct_reason, gspn_resp.hdr.ct_explan); } print_result("Symbolic Port Name", "%s\n", gspn_resp.name); return 0; } #define FC_NS_GSNN_NN 0x0139 static int gsnn_nn(int bsg, u64 wwnn) { struct { struct fc_ct_hdr hdr; u8 len; char name[255]; } __attribute__((__packed__)) gsnn_resp; struct { struct fc_ct_hdr hdr; u64 wwnn; } __attribute__((__packed__)) gsnn = { .hdr = { .ct_rev = FC_CT_REV, .ct_fs_type = FC_FST_DIR, .ct_fs_subtype = FC_NS_SUBTYPE, .ct_cmd = htons(FC_NS_GSNN_NN), .ct_mr_size = htons(sizeof(gsnn_resp)), .ct_vendor = 0, }, .wwnn = htonll(wwnn), }; if (ns_query(bsg, &gsnn, sizeof(gsnn), &gsnn_resp, sizeof(gsnn_resp)) < 0) { print_err("%s ioctl failed: %s\n", __func__, strerror(errno)); return ~0; } if (gsnn_resp.hdr.ct_cmd != htons(FC_FS_ACC)) { print_err("%s command failed: %s, %s\n", __func__, rjt_reason[gsnn_resp.hdr.ct_reason], rjt_explan[gsnn_resp.hdr.ct_explan]); return ct_rjt(gsnn_resp.hdr.ct_reason, gsnn_resp.hdr.ct_explan); } print_result("Symbolic Node Name", "%s\n", gsnn_resp.name); return 0; } enum commands { NONE = 0, GPN_ID, GNN_ID, GSPN_ID, GSNN_NN, }; static const struct option options[] = { { "gpn", required_argument, NULL, GPN_ID }, { "gnn", required_argument, NULL, GNN_ID }, { "gspn", required_argument, NULL, GSPN_ID }, { "gsnn", required_argument, NULL, GSNN_NN }, { "quiet", no_argument, NULL, 'q' }, { NULL, 0, NULL, 0 }, }; static void help(int status) { printf( "Usage: fcnsq [options]\n" "Commands:\n" " --gpn \n" " --gnn \n" " --gspn \n" " --gsnn \n" "Options:\n" " --quiet|-q print minimal results on success, and no error messages\n" "\n" "Port IDs and World Wide Names must be specified in hexadecimal.\n" ); exit(status); } int main(int argc, char *argv[]) { char *bsg; int bsg_dev; u32 port_id = 0; u64 wwnn = 0; int rc = 0; enum commands cmd = 0; char c; uintmax_t wwnn_tmp = 0; while(1) { c = getopt_long_only(argc, argv, "", options, NULL); if (c < 0) break; switch(c) { case '?': help(-1); break; case 'q': quiet = true; break; case GPN_ID: case GNN_ID: case GSPN_ID: if (cmd) help(-1); cmd = c; if (sscanf(optarg, "%x", &port_id) != 1) help(-1); break; case GSNN_NN: if (cmd) help(-1); cmd = c; if (sscanf(optarg, "%jx", &wwnn_tmp) == 1) wwnn = (u64)wwnn_tmp; else help(-1); break; } } if (cmd == NONE) help(-1); if (asprintf(&bsg, "/dev/bsg/fc_%s", argv[optind]) < 0) { if (!quiet) perror(NULL); return -1; } bsg_dev = open(bsg, O_RDWR); if (bsg_dev < 0) { if (!quiet) perror(bsg); return -1; } switch (cmd) { case GPN_ID: rc = gpn_id(bsg_dev, port_id); break; case GNN_ID: rc = gnn_id(bsg_dev, port_id); break; case GSPN_ID: rc = gspn_id(bsg_dev, port_id); break; case GSNN_NN: rc = gsnn_nn(bsg_dev, wwnn); break; default: help(-1); break; }; close(bsg_dev); free(bsg); return rc; }