Back to home page

OSCL-LXR

 
 

    


0001 // SPDX-License-Identifier: GPL-2.0-or-later
0002 /*******************************************************************************
0003  * Filename:  target_core_fabric_lib.c
0004  *
0005  * This file contains generic high level protocol identifier and PR
0006  * handlers for TCM fabric modules
0007  *
0008  * (c) Copyright 2010-2013 Datera, Inc.
0009  *
0010  * Nicholas A. Bellinger <nab@linux-iscsi.org>
0011  *
0012  ******************************************************************************/
0013 
0014 /*
0015  * See SPC4, section 7.5 "Protocol specific parameters" for details
0016  * on the formats implemented in this file.
0017  */
0018 
0019 #include <linux/kernel.h>
0020 #include <linux/string.h>
0021 #include <linux/ctype.h>
0022 #include <linux/spinlock.h>
0023 #include <linux/export.h>
0024 #include <asm/unaligned.h>
0025 
0026 #include <scsi/scsi_proto.h>
0027 
0028 #include <target/target_core_base.h>
0029 #include <target/target_core_fabric.h>
0030 
0031 #include "target_core_internal.h"
0032 #include "target_core_pr.h"
0033 
0034 
0035 static int sas_get_pr_transport_id(
0036     struct se_node_acl *nacl,
0037     int *format_code,
0038     unsigned char *buf)
0039 {
0040     int ret;
0041 
0042     /* Skip over 'naa. prefix */
0043     ret = hex2bin(&buf[4], &nacl->initiatorname[4], 8);
0044     if (ret) {
0045         pr_debug("%s: invalid hex string\n", __func__);
0046         return ret;
0047     }
0048 
0049     return 24;
0050 }
0051 
0052 static int fc_get_pr_transport_id(
0053     struct se_node_acl *se_nacl,
0054     int *format_code,
0055     unsigned char *buf)
0056 {
0057     unsigned char *ptr;
0058     int i, ret;
0059     u32 off = 8;
0060 
0061     /*
0062      * We convert the ASCII formatted N Port name into a binary
0063      * encoded TransportID.
0064      */
0065     ptr = &se_nacl->initiatorname[0];
0066     for (i = 0; i < 23; ) {
0067         if (!strncmp(&ptr[i], ":", 1)) {
0068             i++;
0069             continue;
0070         }
0071         ret = hex2bin(&buf[off++], &ptr[i], 1);
0072         if (ret < 0) {
0073             pr_debug("%s: invalid hex string\n", __func__);
0074             return ret;
0075         }
0076         i += 2;
0077     }
0078     /*
0079      * The FC Transport ID is a hardcoded 24-byte length
0080      */
0081     return 24;
0082 }
0083 
0084 static int sbp_get_pr_transport_id(
0085     struct se_node_acl *nacl,
0086     int *format_code,
0087     unsigned char *buf)
0088 {
0089     int ret;
0090 
0091     ret = hex2bin(&buf[8], nacl->initiatorname, 8);
0092     if (ret) {
0093         pr_debug("%s: invalid hex string\n", __func__);
0094         return ret;
0095     }
0096 
0097     return 24;
0098 }
0099 
0100 static int srp_get_pr_transport_id(
0101     struct se_node_acl *nacl,
0102     int *format_code,
0103     unsigned char *buf)
0104 {
0105     const char *p;
0106     unsigned len, count, leading_zero_bytes;
0107     int rc;
0108 
0109     p = nacl->initiatorname;
0110     if (strncasecmp(p, "0x", 2) == 0)
0111         p += 2;
0112     len = strlen(p);
0113     if (len % 2)
0114         return -EINVAL;
0115 
0116     count = min(len / 2, 16U);
0117     leading_zero_bytes = 16 - count;
0118     memset(buf + 8, 0, leading_zero_bytes);
0119     rc = hex2bin(buf + 8 + leading_zero_bytes, p, count);
0120     if (rc < 0) {
0121         pr_debug("hex2bin failed for %s: %d\n", p, rc);
0122         return rc;
0123     }
0124 
0125     return 24;
0126 }
0127 
0128 static int iscsi_get_pr_transport_id(
0129     struct se_node_acl *se_nacl,
0130     struct t10_pr_registration *pr_reg,
0131     int *format_code,
0132     unsigned char *buf)
0133 {
0134     u32 off = 4, padding = 0;
0135     int isid_len;
0136     u16 len = 0;
0137 
0138     spin_lock_irq(&se_nacl->nacl_sess_lock);
0139     /*
0140      * Only null terminate the last field.
0141      *
0142      * From spc4r37 section 7.6.4.6: TransportID for initiator ports using
0143      * SCSI over iSCSI.
0144      *
0145      * Table 507 TPID=0 Initiator device TransportID
0146      *
0147      * The null-terminated, null-padded (see 4.3.2) ISCSI NAME field shall
0148      * contain the iSCSI name of an iSCSI initiator node (see RFC 7143).
0149      * The first ISCSI NAME field byte containing an ASCII null character
0150      * terminates the ISCSI NAME field without regard for the specified
0151      * length of the iSCSI TransportID or the contents of the ADDITIONAL
0152      * LENGTH field.
0153      */
0154     len = sprintf(&buf[off], "%s", se_nacl->initiatorname);
0155     off += len;
0156     if ((*format_code == 1) && (pr_reg->isid_present_at_reg)) {
0157         /*
0158          * Set FORMAT CODE 01b for iSCSI Initiator port TransportID
0159          * format.
0160          */
0161         buf[0] |= 0x40;
0162         /*
0163          * From spc4r37 Section 7.6.4.6
0164          *
0165          * Table 508 TPID=1 Initiator port TransportID.
0166          *
0167          * The ISCSI NAME field shall not be null-terminated
0168          * (see 4.3.2) and shall not be padded.
0169          *
0170          * The SEPARATOR field shall contain the five ASCII
0171          * characters ",i,0x".
0172          *
0173          * The null-terminated, null-padded ISCSI INITIATOR SESSION ID
0174          * field shall contain the iSCSI initiator session identifier
0175          * (see RFC 3720) in the form of ASCII characters that are the
0176          * hexadecimal digits converted from the binary iSCSI initiator
0177          * session identifier value. The first ISCSI INITIATOR SESSION
0178          * ID field byte containing an ASCII null character terminates
0179          * the ISCSI INITIATOR SESSION ID field without regard for the
0180          * specified length of the iSCSI TransportID or the contents
0181          * of the ADDITIONAL LENGTH field.
0182          */
0183         buf[off++] = 0x2c; /* ASCII Character: "," */
0184         buf[off++] = 0x69; /* ASCII Character: "i" */
0185         buf[off++] = 0x2c; /* ASCII Character: "," */
0186         buf[off++] = 0x30; /* ASCII Character: "0" */
0187         buf[off++] = 0x78; /* ASCII Character: "x" */
0188         len += 5;
0189 
0190         isid_len = sprintf(buf + off, "%s", pr_reg->pr_reg_isid);
0191         off += isid_len;
0192         len += isid_len;
0193     }
0194     buf[off] = '\0';
0195     len += 1;
0196     spin_unlock_irq(&se_nacl->nacl_sess_lock);
0197     /*
0198      * The ADDITIONAL LENGTH field specifies the number of bytes that follow
0199      * in the TransportID. The additional length shall be at least 20 and
0200      * shall be a multiple of four.
0201     */
0202     padding = ((-len) & 3);
0203     if (padding != 0)
0204         len += padding;
0205 
0206     put_unaligned_be16(len, &buf[2]);
0207     /*
0208      * Increment value for total payload + header length for
0209      * full status descriptor
0210      */
0211     len += 4;
0212 
0213     return len;
0214 }
0215 
0216 static int iscsi_get_pr_transport_id_len(
0217     struct se_node_acl *se_nacl,
0218     struct t10_pr_registration *pr_reg,
0219     int *format_code)
0220 {
0221     u32 len = 0, padding = 0;
0222 
0223     spin_lock_irq(&se_nacl->nacl_sess_lock);
0224     len = strlen(se_nacl->initiatorname);
0225     /*
0226      * Add extra byte for NULL terminator
0227      */
0228     len++;
0229     /*
0230      * If there is ISID present with the registration, use format code:
0231      * 01b: iSCSI Initiator port TransportID format
0232      *
0233      * If there is not an active iSCSI session, use format code:
0234      * 00b: iSCSI Initiator device TransportID format
0235      */
0236     if (pr_reg->isid_present_at_reg) {
0237         len += 5; /* For ",i,0x" ASCII separator */
0238         len += strlen(pr_reg->pr_reg_isid);
0239         *format_code = 1;
0240     } else
0241         *format_code = 0;
0242     spin_unlock_irq(&se_nacl->nacl_sess_lock);
0243     /*
0244      * The ADDITIONAL LENGTH field specifies the number of bytes that follow
0245      * in the TransportID. The additional length shall be at least 20 and
0246      * shall be a multiple of four.
0247      */
0248     padding = ((-len) & 3);
0249     if (padding != 0)
0250         len += padding;
0251     /*
0252      * Increment value for total payload + header length for
0253      * full status descriptor
0254      */
0255     len += 4;
0256 
0257     return len;
0258 }
0259 
0260 static char *iscsi_parse_pr_out_transport_id(
0261     struct se_portal_group *se_tpg,
0262     char *buf,
0263     u32 *out_tid_len,
0264     char **port_nexus_ptr)
0265 {
0266     char *p;
0267     int i;
0268     u8 format_code = (buf[0] & 0xc0);
0269     /*
0270      * Check for FORMAT CODE 00b or 01b from spc4r17, section 7.5.4.6:
0271      *
0272      *       TransportID for initiator ports using SCSI over iSCSI,
0273      *       from Table 388 -- iSCSI TransportID formats.
0274      *
0275      *    00b     Initiator port is identified using the world wide unique
0276      *            SCSI device name of the iSCSI initiator
0277      *            device containing the initiator port (see table 389).
0278      *    01b     Initiator port is identified using the world wide unique
0279      *            initiator port identifier (see table 390).10b to 11b
0280      *            Reserved
0281      */
0282     if ((format_code != 0x00) && (format_code != 0x40)) {
0283         pr_err("Illegal format code: 0x%02x for iSCSI"
0284             " Initiator Transport ID\n", format_code);
0285         return NULL;
0286     }
0287     /*
0288      * If the caller wants the TransportID Length, we set that value for the
0289      * entire iSCSI Tarnsport ID now.
0290      */
0291     if (out_tid_len) {
0292         /* The shift works thanks to integer promotion rules */
0293         *out_tid_len = get_unaligned_be16(&buf[2]);
0294         /* Add four bytes for iSCSI Transport ID header */
0295         *out_tid_len += 4;
0296     }
0297 
0298     /*
0299      * Check for ',i,0x' separator between iSCSI Name and iSCSI Initiator
0300      * Session ID as defined in Table 390 - iSCSI initiator port TransportID
0301      * format.
0302      */
0303     if (format_code == 0x40) {
0304         p = strstr(&buf[4], ",i,0x");
0305         if (!p) {
0306             pr_err("Unable to locate \",i,0x\" separator"
0307                 " for Initiator port identifier: %s\n",
0308                 &buf[4]);
0309             return NULL;
0310         }
0311         *p = '\0'; /* Terminate iSCSI Name */
0312         p += 5; /* Skip over ",i,0x" separator */
0313 
0314         *port_nexus_ptr = p;
0315         /*
0316          * Go ahead and do the lower case conversion of the received
0317          * 12 ASCII characters representing the ISID in the TransportID
0318          * for comparison against the running iSCSI session's ISID from
0319          * iscsi_target.c:lio_sess_get_initiator_sid()
0320          */
0321         for (i = 0; i < 12; i++) {
0322             /*
0323              * The first ISCSI INITIATOR SESSION ID field byte
0324              * containing an ASCII null character terminates the
0325              * ISCSI INITIATOR SESSION ID field without regard for
0326              * the specified length of the iSCSI TransportID or the
0327              * contents of the ADDITIONAL LENGTH field.
0328              */
0329             if (*p == '\0')
0330                 break;
0331 
0332             if (isdigit(*p)) {
0333                 p++;
0334                 continue;
0335             }
0336             *p = tolower(*p);
0337             p++;
0338         }
0339     } else
0340         *port_nexus_ptr = NULL;
0341 
0342     return &buf[4];
0343 }
0344 
0345 int target_get_pr_transport_id_len(struct se_node_acl *nacl,
0346         struct t10_pr_registration *pr_reg, int *format_code)
0347 {
0348     switch (nacl->se_tpg->proto_id) {
0349     case SCSI_PROTOCOL_FCP:
0350     case SCSI_PROTOCOL_SBP:
0351     case SCSI_PROTOCOL_SRP:
0352     case SCSI_PROTOCOL_SAS:
0353         break;
0354     case SCSI_PROTOCOL_ISCSI:
0355         return iscsi_get_pr_transport_id_len(nacl, pr_reg, format_code);
0356     default:
0357         pr_err("Unknown proto_id: 0x%02x\n", nacl->se_tpg->proto_id);
0358         return -EINVAL;
0359     }
0360 
0361     /*
0362      * Most transports use a fixed length 24 byte identifier.
0363      */
0364     *format_code = 0;
0365     return 24;
0366 }
0367 
0368 int target_get_pr_transport_id(struct se_node_acl *nacl,
0369         struct t10_pr_registration *pr_reg, int *format_code,
0370         unsigned char *buf)
0371 {
0372     switch (nacl->se_tpg->proto_id) {
0373     case SCSI_PROTOCOL_SAS:
0374         return sas_get_pr_transport_id(nacl, format_code, buf);
0375     case SCSI_PROTOCOL_SBP:
0376         return sbp_get_pr_transport_id(nacl, format_code, buf);
0377     case SCSI_PROTOCOL_SRP:
0378         return srp_get_pr_transport_id(nacl, format_code, buf);
0379     case SCSI_PROTOCOL_FCP:
0380         return fc_get_pr_transport_id(nacl, format_code, buf);
0381     case SCSI_PROTOCOL_ISCSI:
0382         return iscsi_get_pr_transport_id(nacl, pr_reg, format_code,
0383                 buf);
0384     default:
0385         pr_err("Unknown proto_id: 0x%02x\n", nacl->se_tpg->proto_id);
0386         return -EINVAL;
0387     }
0388 }
0389 
0390 const char *target_parse_pr_out_transport_id(struct se_portal_group *tpg,
0391         char *buf, u32 *out_tid_len, char **port_nexus_ptr)
0392 {
0393     u32 offset;
0394 
0395     switch (tpg->proto_id) {
0396     case SCSI_PROTOCOL_SAS:
0397         /*
0398          * Assume the FORMAT CODE 00b from spc4r17, 7.5.4.7 TransportID
0399          * for initiator ports using SCSI over SAS Serial SCSI Protocol.
0400          */
0401         offset = 4;
0402         break;
0403     case SCSI_PROTOCOL_SBP:
0404     case SCSI_PROTOCOL_SRP:
0405     case SCSI_PROTOCOL_FCP:
0406         offset = 8;
0407         break;
0408     case SCSI_PROTOCOL_ISCSI:
0409         return iscsi_parse_pr_out_transport_id(tpg, buf, out_tid_len,
0410                     port_nexus_ptr);
0411     default:
0412         pr_err("Unknown proto_id: 0x%02x\n", tpg->proto_id);
0413         return NULL;
0414     }
0415 
0416     *port_nexus_ptr = NULL;
0417     *out_tid_len = 24;
0418     return buf + offset;
0419 }