/*
* 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 <stdio.h>
#include <stdint.h>
#include <stdlib.h>
#include <stdarg.h>
#include <stdbool.h>
#include <unistd.h>
#include <inttypes.h>
#include <string.h>
#include <errno.h>
#include <fcntl.h>
#include <getopt.h>
#include <arpa/inet.h>
#include <sys/types.h>
#include <sys/ioctl.h>
#include <sys/stat.h>
#include <linux/types.h>
typedef __u8 u8;
typedef __u16 u16;
typedef __u32 u32;
typedef __u64 u64;
#include <linux/bsg.h>
#include <scsi/sg.h>
#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 <host#> <command> [options]\n"
"Commands:\n"
" --gpn <port id>\n"
" --gnn <port id>\n"
" --gspn <port id>\n"
" --gsnn <world wide node name>\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;
}