420 lines · c
1// SPDX-License-Identifier: GPL-2.0-or-later2/*******************************************************************************3 * Filename: target_core_fabric_lib.c4 *5 * This file contains generic high level protocol identifier and PR6 * handlers for TCM fabric modules7 *8 * (c) Copyright 2010-2013 Datera, Inc.9 *10 * Nicholas A. Bellinger <nab@linux-iscsi.org>11 *12 ******************************************************************************/13 14/*15 * See SPC4, section 7.5 "Protocol specific parameters" for details16 * on the formats implemented in this file.17 */18 19#include <linux/kernel.h>20#include <linux/string.h>21#include <linux/ctype.h>22#include <linux/spinlock.h>23#include <linux/export.h>24#include <linux/unaligned.h>25 26#include <scsi/scsi_proto.h>27 28#include <target/target_core_base.h>29#include <target/target_core_fabric.h>30 31#include "target_core_internal.h"32#include "target_core_pr.h"33 34 35static int sas_get_pr_transport_id(36 struct se_node_acl *nacl,37 int *format_code,38 unsigned char *buf)39{40 int ret;41 42 /* Skip over 'naa. prefix */43 ret = hex2bin(&buf[4], &nacl->initiatorname[4], 8);44 if (ret) {45 pr_debug("%s: invalid hex string\n", __func__);46 return ret;47 }48 49 return 24;50}51 52static int fc_get_pr_transport_id(53 struct se_node_acl *se_nacl,54 int *format_code,55 unsigned char *buf)56{57 unsigned char *ptr;58 int i, ret;59 u32 off = 8;60 61 /*62 * We convert the ASCII formatted N Port name into a binary63 * encoded TransportID.64 */65 ptr = &se_nacl->initiatorname[0];66 for (i = 0; i < 23; ) {67 if (!strncmp(&ptr[i], ":", 1)) {68 i++;69 continue;70 }71 ret = hex2bin(&buf[off++], &ptr[i], 1);72 if (ret < 0) {73 pr_debug("%s: invalid hex string\n", __func__);74 return ret;75 }76 i += 2;77 }78 /*79 * The FC Transport ID is a hardcoded 24-byte length80 */81 return 24;82}83 84static int sbp_get_pr_transport_id(85 struct se_node_acl *nacl,86 int *format_code,87 unsigned char *buf)88{89 int ret;90 91 ret = hex2bin(&buf[8], nacl->initiatorname, 8);92 if (ret) {93 pr_debug("%s: invalid hex string\n", __func__);94 return ret;95 }96 97 return 24;98}99 100static int srp_get_pr_transport_id(101 struct se_node_acl *nacl,102 int *format_code,103 unsigned char *buf)104{105 const char *p;106 unsigned len, count, leading_zero_bytes;107 int rc;108 109 p = nacl->initiatorname;110 if (strncasecmp(p, "0x", 2) == 0)111 p += 2;112 len = strlen(p);113 if (len % 2)114 return -EINVAL;115 116 count = min(len / 2, 16U);117 leading_zero_bytes = 16 - count;118 memset(buf + 8, 0, leading_zero_bytes);119 rc = hex2bin(buf + 8 + leading_zero_bytes, p, count);120 if (rc < 0) {121 pr_debug("hex2bin failed for %s: %d\n", p, rc);122 return rc;123 }124 125 return 24;126}127 128static int iscsi_get_pr_transport_id(129 struct se_node_acl *se_nacl,130 struct t10_pr_registration *pr_reg,131 int *format_code,132 unsigned char *buf)133{134 u32 off = 4, padding = 0;135 int isid_len;136 u16 len = 0;137 138 spin_lock_irq(&se_nacl->nacl_sess_lock);139 /*140 * Only null terminate the last field.141 *142 * From spc4r37 section 7.6.4.6: TransportID for initiator ports using143 * SCSI over iSCSI.144 *145 * Table 507 TPID=0 Initiator device TransportID146 *147 * The null-terminated, null-padded (see 4.3.2) ISCSI NAME field shall148 * contain the iSCSI name of an iSCSI initiator node (see RFC 7143).149 * The first ISCSI NAME field byte containing an ASCII null character150 * terminates the ISCSI NAME field without regard for the specified151 * length of the iSCSI TransportID or the contents of the ADDITIONAL152 * LENGTH field.153 */154 len = sprintf(&buf[off], "%s", se_nacl->initiatorname);155 off += len;156 if ((*format_code == 1) && (pr_reg->isid_present_at_reg)) {157 /*158 * Set FORMAT CODE 01b for iSCSI Initiator port TransportID159 * format.160 */161 buf[0] |= 0x40;162 /*163 * From spc4r37 Section 7.6.4.6164 *165 * Table 508 TPID=1 Initiator port TransportID.166 *167 * The ISCSI NAME field shall not be null-terminated168 * (see 4.3.2) and shall not be padded.169 *170 * The SEPARATOR field shall contain the five ASCII171 * characters ",i,0x".172 *173 * The null-terminated, null-padded ISCSI INITIATOR SESSION ID174 * field shall contain the iSCSI initiator session identifier175 * (see RFC 3720) in the form of ASCII characters that are the176 * hexadecimal digits converted from the binary iSCSI initiator177 * session identifier value. The first ISCSI INITIATOR SESSION178 * ID field byte containing an ASCII null character terminates179 * the ISCSI INITIATOR SESSION ID field without regard for the180 * specified length of the iSCSI TransportID or the contents181 * of the ADDITIONAL LENGTH field.182 */183 buf[off++] = 0x2c; /* ASCII Character: "," */184 buf[off++] = 0x69; /* ASCII Character: "i" */185 buf[off++] = 0x2c; /* ASCII Character: "," */186 buf[off++] = 0x30; /* ASCII Character: "0" */187 buf[off++] = 0x78; /* ASCII Character: "x" */188 len += 5;189 190 isid_len = sprintf(buf + off, "%s", pr_reg->pr_reg_isid);191 off += isid_len;192 len += isid_len;193 }194 buf[off] = '\0';195 len += 1;196 spin_unlock_irq(&se_nacl->nacl_sess_lock);197 /*198 * The ADDITIONAL LENGTH field specifies the number of bytes that follow199 * in the TransportID. The additional length shall be at least 20 and200 * shall be a multiple of four.201 */202 padding = ((-len) & 3);203 if (padding != 0)204 len += padding;205 206 put_unaligned_be16(len, &buf[2]);207 /*208 * Increment value for total payload + header length for209 * full status descriptor210 */211 len += 4;212 213 return len;214}215 216static int iscsi_get_pr_transport_id_len(217 struct se_node_acl *se_nacl,218 struct t10_pr_registration *pr_reg,219 int *format_code)220{221 u32 len = 0, padding = 0;222 223 spin_lock_irq(&se_nacl->nacl_sess_lock);224 len = strlen(se_nacl->initiatorname);225 /*226 * Add extra byte for NULL terminator227 */228 len++;229 /*230 * If there is ISID present with the registration, use format code:231 * 01b: iSCSI Initiator port TransportID format232 *233 * If there is not an active iSCSI session, use format code:234 * 00b: iSCSI Initiator device TransportID format235 */236 if (pr_reg->isid_present_at_reg) {237 len += 5; /* For ",i,0x" ASCII separator */238 len += strlen(pr_reg->pr_reg_isid);239 *format_code = 1;240 } else241 *format_code = 0;242 spin_unlock_irq(&se_nacl->nacl_sess_lock);243 /*244 * The ADDITIONAL LENGTH field specifies the number of bytes that follow245 * in the TransportID. The additional length shall be at least 20 and246 * shall be a multiple of four.247 */248 padding = ((-len) & 3);249 if (padding != 0)250 len += padding;251 /*252 * Increment value for total payload + header length for253 * full status descriptor254 */255 len += 4;256 257 return len;258}259 260static char *iscsi_parse_pr_out_transport_id(261 struct se_portal_group *se_tpg,262 char *buf,263 u32 *out_tid_len,264 char **port_nexus_ptr)265{266 char *p;267 int i;268 u8 format_code = (buf[0] & 0xc0);269 /*270 * Check for FORMAT CODE 00b or 01b from spc4r17, section 7.5.4.6:271 *272 * TransportID for initiator ports using SCSI over iSCSI,273 * from Table 388 -- iSCSI TransportID formats.274 *275 * 00b Initiator port is identified using the world wide unique276 * SCSI device name of the iSCSI initiator277 * device containing the initiator port (see table 389).278 * 01b Initiator port is identified using the world wide unique279 * initiator port identifier (see table 390).10b to 11b280 * Reserved281 */282 if ((format_code != 0x00) && (format_code != 0x40)) {283 pr_err("Illegal format code: 0x%02x for iSCSI"284 " Initiator Transport ID\n", format_code);285 return NULL;286 }287 /*288 * If the caller wants the TransportID Length, we set that value for the289 * entire iSCSI Tarnsport ID now.290 */291 if (out_tid_len) {292 /* The shift works thanks to integer promotion rules */293 *out_tid_len = get_unaligned_be16(&buf[2]);294 /* Add four bytes for iSCSI Transport ID header */295 *out_tid_len += 4;296 }297 298 /*299 * Check for ',i,0x' separator between iSCSI Name and iSCSI Initiator300 * Session ID as defined in Table 390 - iSCSI initiator port TransportID301 * format.302 */303 if (format_code == 0x40) {304 p = strstr(&buf[4], ",i,0x");305 if (!p) {306 pr_err("Unable to locate \",i,0x\" separator"307 " for Initiator port identifier: %s\n",308 &buf[4]);309 return NULL;310 }311 *p = '\0'; /* Terminate iSCSI Name */312 p += 5; /* Skip over ",i,0x" separator */313 314 *port_nexus_ptr = p;315 /*316 * Go ahead and do the lower case conversion of the received317 * 12 ASCII characters representing the ISID in the TransportID318 * for comparison against the running iSCSI session's ISID from319 * iscsi_target.c:lio_sess_get_initiator_sid()320 */321 for (i = 0; i < 12; i++) {322 /*323 * The first ISCSI INITIATOR SESSION ID field byte324 * containing an ASCII null character terminates the325 * ISCSI INITIATOR SESSION ID field without regard for326 * the specified length of the iSCSI TransportID or the327 * contents of the ADDITIONAL LENGTH field.328 */329 if (*p == '\0')330 break;331 332 if (isdigit(*p)) {333 p++;334 continue;335 }336 *p = tolower(*p);337 p++;338 }339 } else340 *port_nexus_ptr = NULL;341 342 return &buf[4];343}344 345int target_get_pr_transport_id_len(struct se_node_acl *nacl,346 struct t10_pr_registration *pr_reg, int *format_code)347{348 switch (nacl->se_tpg->proto_id) {349 case SCSI_PROTOCOL_FCP:350 case SCSI_PROTOCOL_SBP:351 case SCSI_PROTOCOL_SRP:352 case SCSI_PROTOCOL_SAS:353 break;354 case SCSI_PROTOCOL_ISCSI:355 return iscsi_get_pr_transport_id_len(nacl, pr_reg, format_code);356 default:357 pr_err("Unknown proto_id: 0x%02x\n", nacl->se_tpg->proto_id);358 return -EINVAL;359 }360 361 /*362 * Most transports use a fixed length 24 byte identifier.363 */364 *format_code = 0;365 return 24;366}367 368int target_get_pr_transport_id(struct se_node_acl *nacl,369 struct t10_pr_registration *pr_reg, int *format_code,370 unsigned char *buf)371{372 switch (nacl->se_tpg->proto_id) {373 case SCSI_PROTOCOL_SAS:374 return sas_get_pr_transport_id(nacl, format_code, buf);375 case SCSI_PROTOCOL_SBP:376 return sbp_get_pr_transport_id(nacl, format_code, buf);377 case SCSI_PROTOCOL_SRP:378 return srp_get_pr_transport_id(nacl, format_code, buf);379 case SCSI_PROTOCOL_FCP:380 return fc_get_pr_transport_id(nacl, format_code, buf);381 case SCSI_PROTOCOL_ISCSI:382 return iscsi_get_pr_transport_id(nacl, pr_reg, format_code,383 buf);384 default:385 pr_err("Unknown proto_id: 0x%02x\n", nacl->se_tpg->proto_id);386 return -EINVAL;387 }388}389 390const char *target_parse_pr_out_transport_id(struct se_portal_group *tpg,391 char *buf, u32 *out_tid_len, char **port_nexus_ptr)392{393 u32 offset;394 395 switch (tpg->proto_id) {396 case SCSI_PROTOCOL_SAS:397 /*398 * Assume the FORMAT CODE 00b from spc4r17, 7.5.4.7 TransportID399 * for initiator ports using SCSI over SAS Serial SCSI Protocol.400 */401 offset = 4;402 break;403 case SCSI_PROTOCOL_SBP:404 case SCSI_PROTOCOL_SRP:405 case SCSI_PROTOCOL_FCP:406 offset = 8;407 break;408 case SCSI_PROTOCOL_ISCSI:409 return iscsi_parse_pr_out_transport_id(tpg, buf, out_tid_len,410 port_nexus_ptr);411 default:412 pr_err("Unknown proto_id: 0x%02x\n", tpg->proto_id);413 return NULL;414 }415 416 *port_nexus_ptr = NULL;417 *out_tid_len = 24;418 return buf + offset;419}420