RFC 3720

Internet Small Computer Systems Interface (iSCSI)

๐Ÿ“… published April 2004, from ietf

Internet Small Computer Systems Interface (iSCSI) ๐Ÿ’–

okay bestie, let's get the vibes straight first ๐ŸŽ€ this is RFC 3720, a Standards Track doc from the Network Working Group, dated April 2004 ๐ŸŒธ. The authors are J. Satran, K. Meth, and E. Zeidner from IBM, C. Sapuntzakis from Cisco Systems, and M. Chadalapaka from Hewlett-Packard Co. ๐Ÿ‘‘ The title block doesn't say it obsoletes or updates anything, so it's giving main character energy ๐Ÿฆ‹


Status of this Memo โœจ

okay so this section is the official disclaimer ๐Ÿ’… It specifies an Internet standards track protocol for the whole Internet community, and the authors are literally asking for discussion and suggestions for improvements ๐Ÿซถ For the current standardization state and status of this protocol, check the "Internet Official Protocol Standards" (STD 1) list ๐Ÿ“š And yes, distribution of this memo is unlimited, no cap ๐ŸŒˆ

the copyright line is Copyright (C) The Internet Society (2003). All Rights Reserved. and that's the whole tea ๐Ÿซ 

Abstract ๐Ÿฆ‹

so this doc describes a transport protocol for Internet Small Computer Systems Interface (iSCSI) that runs on top of TCP ๐Ÿš€ The iSCSI protocol is aiming to be fully compliant with the standardized SCSI architecture model, which is so iconic ๐Ÿ’…

SCSI is a popular family of protocols that let systems talk to I/O devices, especially storage devices ๐Ÿ’พ SCSI protocols are request/response application protocols. They share a common standardized architecture model and basic command set, plus standardized command sets for different device classes (disks, tapes, media-changers etc.) ๐Ÿ“

as system interconnects move from the classic bus structure to a network structure, SCSI has to get mapped onto network transport protocols ๐ŸŒ IP networks now meet the performance requirements of fast system interconnects, so they're good candidates to "carry" SCSI ๐Ÿงš

Table of Contents ๐ŸŒธ

Part 2 should start at Section 1, but the text you sent is the table of contents, which was already covered in part 1. I don't have the body text from Section 1 onward, and I can't rewrite it reliably from memory without risking changes to requirements, numbers, or examples.

Could you paste the RFC 3720 text starting at "1. Introduction" and continuing as far as you'd like for this part? I'll pick up right there in the same format.

okay bestie, the table of contents keeps going, and it's giving a lot of sections ๐Ÿ“‘โœจ

       9.1.   Multiple Network Adapters. . . . . . . . . . . . . . . 106
              9.1.1.    Conservative Reuse of ISIDs. . . . . . . . . 107
              9.1.2.    iSCSI Name, ISID, and TPGT Use . . . . . . . 107
       9.2.   Autosense and Auto Contingent Allegiance (ACA) . . . . 109
       9.3.   iSCSI Timeouts . . . . . . . . . . . . . . . . . . . . 109
       9.4.   Command Retry and Cleaning Old Command Instances . . . 110
       9.5.   Synch and Steering Layer and Performance . . . . . . . 110
       9.6.   Considerations for State-dependent Devices and
              Long-lasting SCSI Operations . . . . . . . . . . . . . 111
              9.6.1.    Determining the Proper ErrorRecoveryLevel. . 112
   10. iSCSI PDU Formats . . . . . . . . . . . . . . . . . . . . . . 112
       10.1.  iSCSI PDU Length and Padding . . . . . . . . . . . . . 113
       10.2.  PDU Template, Header, and Opcodes. . . . . . . . . . . 113
              10.2.1.   Basic Header Segment (BHS) . . . . . . . . . 114
                        10.2.1.1.  I . . . . . . . . . . . . . . . . 115
                        10.2.1.2.  Opcode. . . . . . . . . . . . . . 115
                        10.2.1.3.  Final (F) bit . . . . . . . . . . 116
                        10.2.1.4.  Opcode-specific Fields. . . . . . 116
                        10.2.1.5.  TotalAHSLength. . . . . . . . . . 116
                        10.2.1.6.  DataSegmentLength . . . . . . . . 116
                        10.2.1.7.  LUN . . . . . . . . . . . . . . . 116
                        10.2.1.8.  Initiator Task Tag. . . . . . . . 117
              10.2.2.  Additional Header Segment (AHS) . . . . . . . 117
                        10.2.2.1.  AHSType . . . . . . . . . . . . . 117
                        10.2.2.2.  AHSLength . . . . . . . . . . . . 117
                        10.2.2.3.  Extended CDB AHS. . . . . . . . . 118
                        10.2.2.4.  Bidirectional Expected Read-Data
                                   Length AHS. . . . . . . . . . . . 118
              10.2.3.   Header Digest and Data Digest. . . . . . . . 118
              10.2.4.   Data Segment . . . . . . . . . . . . . . . . 119
       10.3.  SCSI Command . . . . . . . . . . . . . . . . . . . . . 119
              10.3.1.   Flags and Task Attributes (byte 1) . . . . . 120
              10.3.2.   CmdSN - Command Sequence Number. . . . . . . 120
              10.3.3.   ExpStatSN. . . . . . . . . . . . . . . . . . 120
              10.3.4.   Expected Data Transfer Length. . . . . . . . 121
              10.3.5.   CDB - SCSI Command Descriptor Block. . . . . 121
              10.3.6.   Data Segment - Command Data. . . . . . . . . 121
       10.4.  SCSI Response. . . . . . . . . . . . . . . . . . . . . 122
              10.4.1.   Flags (byte 1) . . . . . . . . . . . . . . . 123
              10.4.2.   Status . . . . . . . . . . . . . . . . . . . 123
              10.4.3.   Response . . . . . . . . . . . . . . . . . . 124
              10.4.4.   SNACK Tag. . . . . . . . . . . . . . . . . . 125
              10.4.5.   Residual Count . . . . . . . . . . . . . . . 125
              10.4.6.   Bidirectional Read Residual Count. . . . . . 125
              10.4.7.   Data Segment - Sense and Response Data
                        Segment. . . . . . . . . . . . . . . . . . . 125
                        10.4.7.1.  SenseLength . . . . . . . . . . . 126
                        10.4.7.2.  Sense Data. . . . . . . . . . . . 126

              10.4.8.   ExpDataSN. . . . . . . . . . . . . . . . . . 127
              10.4.9.   StatSN - Status Sequence Number. . . . . . . 127
              10.4.10.  ExpCmdSN - Next Expected CmdSN from this
                        Initiator. . . . . . . . . . . . . . . . . . 128
              10.4.11.  MaxCmdSN - Maximum CmdSN from this Initiator 128
       10.5.  Task Management Function Request . . . . . . . . . . . 129
              10.5.1.   Function . . . . . . . . . . . . . . . . . . 129
              10.5.2.   TotalAHSLength and DataSegmentLength . . . . 132
              10.5.3.   LUN. . . . . . . . . . . . . . . . . . . . . 132
              10.5.4.   Referenced Task Tag. . . . . . . . . . . . . 132
              10.5.5.   RefCmdSN . . . . . . . . . . . . . . . . . . 132
              10.5.6.   ExpDataSN. . . . . . . . . . . . . . . . . . 133
       10.6.  Task Management Function Response. . . . . . . . . . . 134
              10.6.1.   Response . . . . . . . . . . . . . . . . . . 134
              10.6.2.   Task Management Actions on Task Sets . . . . 136
              10.6.3.   TotalAHSLength and DataSegmentLength . . . . 137
       10.7.  SCSI Data-Out & SCSI Data-In . . . . . . . . . . . . . 137
              10.7.1.   F (Final) Bit. . . . . . . . . . . . . . . . 139
              10.7.2.   A (Acknowledge) Bit. . . . . . . . . . . . . 139
              10.7.3.   Flags (byte 1) . . . . . . . . . . . . . . . 140
              10.7.4.   Target Transfer Tag and LUN. . . . . . . . . 140
              10.7.5.   DataSN . . . . . . . . . . . . . . . . . . . 141
              10.7.6.   Buffer Offset. . . . . . . . . . . . . . . . 141
              10.7.7.   DataSegmentLength. . . . . . . . . . . . . . 141
       10.8.  Ready To Transfer (R2T). . . . . . . . . . . . . . . . 142
              10.8.1.   TotalAHSLength and DataSegmentLength . . . . 143
              10.8.2.   R2TSN. . . . . . . . . . . . . . . . . . . . 143
              10.8.3.   StatSN . . . . . . . . . . . . . . . . . . . 144
              10.8.4.   Desired Data Transfer Length and Buffer
                        Offset . . . . . . . . . . . . . . . . . . . 144
              10.8.5.   Target Transfer Tag. . . . . . . . . . . . . 144
       10.9.  Asynchronous Message . . . . . . . . . . . . . . . . . 145
              10.9.1.   AsyncEvent . . . . . . . . . . . . . . . . . 146
              10.9.2.   AsyncVCode . . . . . . . . . . . . . . . . . 147
              10.9.3.   LUN. . . . . . . . . . . . . . . . . . . . . 147
              10.9.4.   Sense Data and iSCSI Event Data. . . . . . . 148
                        10.9.4.1.  SenseLength . . . . . . . . . . . 148
       10.10. Text Request . . . . . . . . . . . . . . . . . . . . . 149
              10.10.1.  F (Final) Bit. . . . . . . . . . . . . . . . 150
              10.10.2.  C (Continue) Bit . . . . . . . . . . . . . . 150
              10.10.3.  Initiator Task Tag . . . . . . . . . . . . . 150
              10.10.4.  Target Transfer Tag. . . . . . . . . . . . . 150
              10.10.5.  Text . . . . . . . . . . . . . . . . . . . . 151
       10.11. Text Response. . . . . . . . . . . . . . . . . . . . . 152
              10.11.1.  F (Final) Bit. . . . . . . . . . . . . . . . 152
              10.11.2.  C (Continue) Bit . . . . . . . . . . . . . . 153
              10.11.3.  Initiator Task Tag . . . . . . . . . . . . . 153
              10.11.4.  Target Transfer Tag. . . . . . . . . . . . . 153
              10.11.5.  StatSN . . . . . . . . . . . . . . . . . . . 154
              10.11.6.  Text Response Data . . . . . . . . . . . . . 154
       10.12. Login Request. . . . . . . . . . . . . . . . . . . . . 154
              10.12.1.  T (Transit) Bit. . . . . . . . . . . . . . . 155
              10.12.2.  C (Continue) Bit . . . . . . . . . . . . . . 155
              10.12.3.  CSG and NSG. . . . . . . . . . . . . . . . . 156
              10.12.4.  Version. . . . . . . . . . . . . . . . . . . 156
                        10.12.4.1.  Version-max. . . . . . . . . . . 156
                        10.12.4.2.  Version-min. . . . . . . . . . . 156
              10.12.5.  ISID . . . . . . . . . . . . . . . . . . . . 157
              10.12.6.  TSIH . . . . . . . . . . . . . . . . . . . . 158
              10.12.7.  Connection ID - CID. . . . . . . . . . . . . 158
              10.12.8.  CmdSN. . . . . . . . . . . . . . . . . . . . 159
              10.12.9.  ExpStatSN. . . . . . . . . . . . . . . . . . 159
              10.12.10. Login Parameters . . . . . . . . . . . . . . 159
       10.13. Login Response . . . . . . . . . . . . . . . . . . . . 160
              10.13.1.  Version-max. . . . . . . . . . . . . . . . . 160
              10.13.2.  Version-active . . . . . . . . . . . . . . . 161
              10.13.3.  TSIH . . . . . . . . . . . . . . . . . . . . 161
              10.13.4.  StatSN . . . . . . . . . . . . . . . . . . . 161
              10.13.5.  Status-Class and Status-Detail . . . . . . . 161
              10.13.6.  T (Transit) Bit. . . . . . . . . . . . . . . 164
              10.13.7.  C (Continue) Bit . . . . . . . . . . . . . . 164
              10.13.8.  Login Parameters . . . . . . . . . . . . . . 164
       10.14. Logout Request . . . . . . . . . . . . . . . . . . . . 165
              10.14.1.  Reason Code. . . . . . . . . . . . . . . . . 167
              10.14.2.  TotalAHSLength and DataSegmentLength . . . . 168
              10.14.3.  CID. . . . . . . . . . . . . . . . . . . . . 168
              10.14.4.  ExpStatSN. . . . . . . . . . . . . . . . . . 168
              10.14.5.  Implicit termination of tasks. . . . . . . . 168
       10.15. Logout Response. . . . . . . . . . . . . . . . . . . . 169
              10.15.1.  Response . . . . . . . . . . . . . . . . . . 170
              10.15.2.  TotalAHSLength and DataSegmentLength . . . . 170
              10.15.3.  Time2Wait. . . . . . . . . . . . . . . . . . 170
              10.15.4.  Time2Retain. . . . . . . . . . . . . . . . . 170
       10.16. SNACK Request. . . . . . . . . . . . . . . . . . . . . 171
              10.16.1.  Type . . . . . . . . . . . . . . . . . . . . 172
              10.16.2.  Data Acknowledgement . . . . . . . . . . . . 173
              10.16.3.  Resegmentation . . . . . . . . . . . . . . . 173
              10.16.4.  Initiator Task Tag . . . . . . . . . . . . . 174
              10.16.5.  Target Transfer Tag or SNACK Tag . . . . . . 174
              10.16.6.  BegRun . . . . . . . . . . . . . . . . . . . 174
              10.16.7.  RunLength. . . . . . . . . . . . . . . . . . 174
       10.17. Reject . . . . . . . . . . . . . . . . . . . . . . . . 175
              10.17.1.  Reason . . . . . . . . . . . . . . . . . . . 176
              10.17.2.  DataSN/R2TSN . . . . . . . . . . . . . . . . 177
              10.17.3.  StatSN, ExpCmdSN and MaxCmdSN. . . . . . . . 177
              10.17.4.  Complete Header of Bad PDU . . . . . . . . . 177

1. Introduction โœจ

Okay bestie, let's start with the big picture ๐Ÿฆ‹ The Small Computer Systems Interface (SCSI) is a super popular family of protocols for talking to I/O devices, especially storage devices ๐Ÿ’พ It's a client-server architecture, and the clients are called "initiators" ๐Ÿ’… Initiators send SCSI "commands" to request services from components, which are logical units of a server called a "target" ๐ŸŽฏ A "SCSI transport" is what maps that client-server SCSI protocol onto a specific interconnect ๐Ÿ”Œ So an initiator is one endpoint of a SCSI transport, and a target is the other endpoint ๐Ÿซถ

SCSI has been mapped over lots of transports, including Parallel SCSI, IPI, IEEE-1394 (firewire), and Fibre Channel ๐ŸŒˆ But these transports are I/O specific and have limited distance capabilities ๐Ÿ˜ญ

Now the main event ๐Ÿ’– The iSCSI protocol defined in this document describes a way to carry SCSI packets over TCP/IP (see [RFC791], [RFC793], [RFC1035], [RFC1122]) โœจ That gives us an interoperable solution that can use the existing Internet infrastructure and Internet management facilities, and it can tackle those distance limitations too ๐Ÿงš It's giving main character energy for storage networking, no cap ๐Ÿ‘‘


2. Definitions and Acronyms ๐Ÿ“š

2.1. Definitions โœจ

okay bestie, this is the vocab list everything else in the spec leans on, so let's get it right ๐Ÿ’…๐ŸŽ€

  • Alias: an alias string can also be attached to an iSCSI Node ๐Ÿฆ‹ it lets an organization pin a user-friendly string to the iSCSI Name, but the alias is literally not a substitute for the iSCSI Name ๐Ÿ™ˆ
  • CID (Connection ID): connections inside a session get identified by a connection ID ๐ŸŒธ it's a unique ID for that connection within the session, from the initiator's side. The initiator generates it and presents it to the target during login requests and during logouts that close connections โœจ
  • Connection: a connection is a TCP connection ๐Ÿ’œ the initiator and target talk over one or more TCP connections, and those carry control messages, SCSI commands, parameters, and data inside iSCSI Protocol Data Units (iSCSI PDUs) ๐Ÿซถ
  • iSCSI Device: a SCSI Device using an iSCSI service delivery subsystem ๐Ÿงš the Service Delivery Subsystem is defined by [SAM2] as a transport mechanism for SCSI commands and responses
  • iSCSI Initiator Name: the worldwide unique name of the initiator ๐Ÿ“
  • iSCSI Initiator Node: this is "the initiator" ๐Ÿ‘‘ the word "initiator" gets qualified as either a port or a device in the rest of the document when the context is confusing. Any unqualified "initiator" means an initiator port (or device), depending on the context ๐Ÿค“
  • iSCSI Layer: this layer builds and receives iSCSI PDUs, then relays them to and from one or more TCP connections that form an initiator-target "session" ๐ŸŒˆ
  • iSCSI Name: the name of an iSCSI initiator or iSCSI target โœจ
  • iSCSI Node: a single iSCSI initiator or iSCSI target ๐ŸŽ€ there are one or more iSCSI Nodes within a Network Entity, and each is reachable through one or more Network Portals. An iSCSI Node is identified by its iSCSI Name ๐Ÿฆ‹ because the Name is separate from the addresses, multiple iSCSI Nodes can share one address, and one iSCSI Node can use multiple addresses. iconic ๐Ÿ’…
  • iSCSI Target Name: the worldwide unique name of the target ๐Ÿ’œ
  • iSCSI Target Node: the "target" ๐ŸŽฏ
  • iSCSI Task: an iSCSI request for which a response is expected ๐Ÿ“ฌ
  • iSCSI Transfer Direction: defined with regard to the initiator. Outbound or outgoing transfers go from the initiator to the target, and inbound or incoming transfers go from the target to the initiator ๐Ÿ”ฅ
  • ISID: the initiator part of the Session Identifier, explicitly specified by the initiator during Login ๐ŸŒธ
  • I_T nexus: according to [SAM2], the I_T nexus is a relationship between a SCSI Initiator Port and a SCSI Target Port. For iSCSI, this relationship is a session, defined as a relationship between an iSCSI Initiator's end of the session (SCSI Initiator Port) and the iSCSI Target's Portal Group. The I_T nexus can be identified by the conjunction of the SCSI port names, so the I_T nexus identifier is the tuple (iSCSI Initiator Name + ',i,'+ ISID, iSCSI Target Name + ',t,'+ Portal Group Tag) ๐Ÿงฉ
  • Network Entity: a device or gateway that is accessible from the IP network ๐Ÿงš a Network Entity has one or more Network Portals, and each of those can be used to get onto the IP network by some iSCSI Nodes inside that Network Entity ๐ŸŒ
  • Network Portal: a component of a Network Entity that has a TCP/IP network address and that an iSCSI Node inside that entity may use for the connection(s) within one of its iSCSI sessions. In an initiator, a Network Portal is identified by its IP address. In a target, it's identified by its IP address and its listening TCP port ๐Ÿšช
  • Originator: in a negotiation or exchange, the party that starts it ๐Ÿ’–
  • PDU (Protocol Data Unit): the initiator and target split their communications into messages, and these messages are called "iSCSI protocol data units" (iSCSI PDUs) ๐Ÿ“ฆ
  • Portal Groups: iSCSI supports multiple connections within the same session, and some implementations can combine connections in a session across multiple Network Portals ๐Ÿฆ‹ a Portal Group defines a set of Network Portals within an iSCSI Network Entity that together can coordinate a session with connections spanning those portals. Not every Network Portal in a Portal Group needs to take part in every session connected through that group. One or more Portal Groups may provide access to an iSCSI Node, and each Network Portal, as used by a given iSCSI Node, belongs to exactly one portal group within that node ๐Ÿงบ
  • Portal Group Tag: this 16-bit quantity identifies a Portal Group within an iSCSI Node. All Network Portals with the same portal group tag in the context of a given iSCSI Node are in the same Portal Group ๐Ÿท๏ธ
  • Recovery R2T: an R2T generated by a target after it detects the loss of one or more Data-Out PDUs through a digest error, a sequence error, or a sequence reception timeout ๐Ÿ›Ÿ a recovery R2T carries the next unused R2TSN, but it requests all or part of the data burst that an earlier R2T (with a lower R2TSN) had already requested ๐Ÿ”
  • Responder: in a negotiation or exchange, the party that answers the originator of that negotiation or exchange ๐Ÿซฑ
  • SCSI Device: this is the SAM2 term for an entity that contains one or more SCSI ports connected to a service delivery subsystem and supports a SCSI application protocol ๐Ÿ  for example, a SCSI Initiator Device contains one or more SCSI Initiator Ports and zero or more application clients, while a Target Device contains one or more SCSI Target Ports and one or more device servers and associated logical units. For iSCSI, the SCSI Device is the component within an iSCSI Node that provides the SCSI functionality, so there can be at most, one SCSI Device within a given iSCSI Node. Access to the SCSI Device can only be achieved in an iSCSI normal operational session. The SCSI Device Name is defined to be the iSCSI Name of the node ๐Ÿ‘‘
  • SCSI Layer: this layer builds and receives SCSI CDBs (Command Descriptor Blocks) and relays them, along with the remaining command execute [SAM2] parameters, to and from the iSCSI Layer ๐ŸŽฌ
  • Session: the group of TCP connections that link an initiator with a target forms a session, which is loosely equivalent to a SCSI I-T nexus ๐Ÿงต TCP connections can be added to and removed from a session. Across all connections within a session, an initiator sees one and the same target ๐ŸŽฏ
  • SCSI Initiator Port: this maps to the endpoint of an iSCSI normal operational session. That session is negotiated through the login process between an iSCSI initiator node and an iSCSI target node. When that process completes successfully, a SCSI Initiator Port is created within the SCSI Initiator Device ๐Ÿš€ the SCSI Initiator Port Name and SCSI Initiator Port Identifier are both defined to be the iSCSI Initiator Name together with (a) a label that identifies it as an initiator port name/identifier and (b) the ISID portion of the session identifier ๐ŸŒธ
  • SCSI Port: this is the SAM2 term for an entity in a SCSI Device that provides the SCSI functionality needed to interface with a service delivery subsystem ๐Ÿ”Œ for iSCSI, the definition of the SCSI Initiator Port and the SCSI Target Port are different ๐Ÿ™ƒ
  • SCSI Port Name: a name made up of UTF-8 [RFC2279] characters that includes the iSCSI Name + 'i' or 't' + ISID or Portal Group Tag ๐Ÿท๏ธ
  • SCSI Target Port: this maps to an iSCSI Target Portal Group ๐ŸŽฏ
  • SCSI Target Port Name and SCSI Target Port Identifier: both are defined to be the iSCSI Target Name together with (a) a label that identifies it as a target port name/identifier and (b) the portal group tag ๐Ÿ’œ
  • SSID (Session ID): a session between an iSCSI initiator and an iSCSI target is defined by a session ID, which is a tuple made of an initiator part (ISID) and a target part (Target Portal Group Tag) ๐Ÿงฉ the ISID is explicitly specified by the initiator at session establishment. The Target Portal Group Tag is implied by the initiator through its selection of the TCP endpoint at connection establishment. The TargetPortalGroupTag key must also be returned by the target as a confirmation during connection establishment when TargetName is given ๐Ÿซถ
  • Target Portal Group Tag: a numerical identifier (16-bit) for an iSCSI Target Portal Group ๐Ÿ”ข
  • TSIH (Target Session Identifying Handle): a target assigned tag for a session with a specific named initiator ๐Ÿงš the target generates it during session establishment. Its internal format and content are not defined by this protocol, except for the value 0, which is reserved and used by the initiator to indicate a new session. It is given to the target during additional connection establishment for the same session โœจ

2.2. Acronyms

okay this list is LONG bestie ๐Ÿ˜ญ but every single acronym is here, no cap:

Acronym     Definition
------------------------------------------------------------
3DES        Triple Data Encryption Standard
ACA         Auto Contingent Allegiance
AEN         Asynchronous Event Notification
AES         Advanced Encryption Standard
AH          Additional Header (not the IPsec AH!)
AHS         Additional Header Segment
API         Application Programming Interface
ASC         Additional Sense Code
ASCII       American Standard Code for Information Interchange
ASCQ        Additional Sense Code Qualifier
BHS         Basic Header Segment
CBC         Cipher Block Chaining
CD          Compact Disk
CDB         Command Descriptor Block
CHAP        Challenge Handshake Authentication Protocol
CID         Connection ID
CO          Connection Only
CRC         Cyclic Redundancy Check
CRL         Certificate Revocation List
CSG         Current Stage
CSM         Connection State Machine
DES         Data Encryption Standard
DNS         Domain Name Server
DOI         Domain of Interpretation
DVD         Digital Versatile Disk
ESP         Encapsulating Security Payload
EUI         Extended Unique Identifier
FFP         Full Feature Phase
FFPO        Full Feature Phase Only
FIM         Fixed Interval Marker
Gbps        Gigabits per Second
HBA         Host Bus Adapter
HMAC        Hashed Message Authentication Code
I_T         Initiator_Target
I_T_L       Initiator_Target_LUN
IANA        Internet Assigned Numbers Authority
ID          Identifier
IDN         Internationalized Domain Name
IEEE        Institute of Electrical & Electronics Engineers
IETF        Internet Engineering Task Force
IKE         Internet Key Exchange
I/O         Input - Output
IO          Initialize Only
IP          Internet Protocol
IPsec       Internet Protocol Security
IPv4        Internet Protocol Version 4
IPv6        Internet Protocol Version 6
IQN         iSCSI Qualified Name
ISID        Initiator Session ID
ITN         iSCSI Target Name
ITT         Initiator Task Tag
KRB5        Kerberos V5
LFL         Lower Functional Layer
LTDS        Logical-Text-Data-Segment
LO          Leading Only
LU          Logical Unit
LUN         Logical Unit Number
MAC         Message Authentication Codes
NA          Not Applicable
NIC         Network Interface Card
NOP         No Operation
NSG         Next Stage
OS          Operating System
PDU         Protocol Data Unit
PKI         Public Key Infrastructure
R2T         Ready To Transfer
R2TSN       Ready To Transfer Sequence Number
RDMA        Remote Direct Memory Access
RFC         Request For Comments
SAM         SCSI Architecture Model
SAM2        SCSI Architecture Model - 2
SAN         Storage Area Network
SCSI        Small Computer Systems Interface
SN          Sequence Number
SNACK       Selective Negative Acknowledgment - also
            Sequence Number Acknowledgement for data
SPKM        Simple Public-Key Mechanism
SRP         Secure Remote Password
SSID        Session ID
SW          Session Wide
TCB         Task Control Block
TCP         Transmission Control Protocol
TPGT        Target Portal Group Tag
TSIH        Target Session Identifying Handle
TTT         Target Transfer Tag
UFL         Upper Functional Layer
ULP         Upper Level Protocol
URN         Uniform Resource Names [RFC2396]
UTF         Universal Transformation Format
WG          Working Group

2.3. Conventions

in the examples, I-> and T-> are how we show which iSCSI PDUs come from the initiator and which come from the target ๐Ÿงš

the key words "MUST", "MUST NOT", "REQUIRED", "SHALL", "SHALL NOT", "SHOULD", "SHOULD NOT", "RECOMMENDED", "MAY", and "OPTIONAL" in this document are to be interpreted as described in BCP 14 [RFC2119]. these words are doing a LOT of work so pay attention ๐Ÿ’…

iSCSI messages (PDUs) get drawn with diagrams like this one ๐ŸŽ€

 Byte/     0       |       1       |       2       |       3       |
    /              |               |               |               |
   |0 1 2 3 4 5 6 7|0 1 2 3 4 5 6 7|0 1 2 3 4 5 6 7|0 1 2 3 4 5 6 7|
   +---------------+---------------+---------------+---------------+
  0| Basic Header Segment (BHS)                                    |
   +---------------+---------------+---------------+---------------+
 ----------
  +|                                                               |
   +---------------+---------------+---------------+---------------+

the diagrams include byte and bit numbering, so you can always tell exactly where everything sits ๐Ÿฆ‹

these are the representation and ordering rules this document follows:

  • Word Rule
  • Half-word Rule
  • Byte Rule

2.3.1. Word Rule

a word holds four consecutive bytes โœจ when a word has numeric content, it's read as an unsigned number in base 2 positional representation. the lowest numbered byte (for example, byte 0) has bit 0 representing 231 and bit 1 representing 230, all the way through the lowest numbered byte + 3 (for example, byte 3), where bit 7 represents 2**0. the math is mathing ๐Ÿงฎ

decimal and hexadecimal representations of word values map this same representation onto decimal or hexadecimal positional notation, so the numbers match up no matter how you write them ๐ŸŒธ

2.3.2. Half-Word Rule

a half-word holds two consecutive bytes ๐Ÿ“ when a half-word has numeric content, it's an unsigned number in base 2 positional representation. the lowest numbered byte (for example, byte 0) has bit 0 representing 215 and bit 1 representing 214, all the way through the lowest numbered byte + 1 (for example, byte 1), where bit 7 represents 2**0. it's giving very precise ๐Ÿ™ˆ

decimal and hexadecimal representations of half-word values map this representation onto decimal or hexadecimal positional notation, same energy as the word rule ๐Ÿ’œ

2.3.3. Byte Rule

for every PDU, bytes get sent and received in increasing numbered order, which is called network order ๐ŸŒˆ whenever a byte has numerical content, it's an unsigned number in base 2 positional representation, with bit 0 representing 27, bit 1 representing 26, and so on through bit 7 representing 2**0. ok the byte rule is literally the simplest one and I'm obsessed ๐Ÿ˜ญ


3. Overview

3.1. SCSI Concepts

the SCSI Architecture Model-2 [SAM2] is the big official doc that describes the architecture of the SCSI family of I/O protocols in full detail ๐Ÿ“š this section is just a quick background so you get the terminology before we go deeper ๐Ÿซถ

at the highest level, SCSI is a family of interfaces for requesting services from I/O devices. that includes hard drives, tape drives, CD and DVD drives, printers, and scanners ๐Ÿ–จ๏ธ in SCSI language, each individual I/O device is called a "logical unit" (LU).

SCSI is a client-server architecture. the clients of a SCSI interface are called "initiators", and they issue SCSI "commands" to ask for services from components (logical units) that live on a server known as a "target". the "device server" on the logical unit is the thing that accepts SCSI commands and processes them ๐Ÿ’…

a "SCSI transport" maps the client-server SCSI protocol onto a specific interconnect. initiators are one endpoint of a SCSI transport, and the "target" is the other endpoint ๐Ÿฆ‹ a target can hold multiple Logical Units (LUs), and each one has an address inside its target called a Logical Unit Number (LUN) ๐Ÿ”ฅ

a SCSI task is a SCSI command, or possibly a linked set of SCSI commands. some LUs support multiple pending (queued) tasks, but the logical unit is the one managing that queue, not you ๐Ÿ‘‘ the target uses an initiator provided "task tag" to tell tasks apart. only one command in a task can be outstanding at any given time, so no multitasking chaos here ๐Ÿ™…โ€โ™€๏ธ

each SCSI command results in an optional data phase and a required response phase. in the data phase, info can travel from initiator to target (like a WRITE), from target to initiator (like a READ), or in both directions ๐Ÿ”„ in the response phase, the target sends back the final status of the operation, including any errors ๐Ÿ’€

Command Descriptor Blocks (CDB) are the data structures that hold the command parameters an initiator sends to a target. the CDB content and structure is defined by [SAM2] and by device-type specific SCSI standards ๐Ÿ“‹

3.2. iSCSI Concepts and Functional Overview

the iSCSI protocol maps the SCSI remote procedure invocation model (see [SAM2]) over the TCP protocol. SCSI commands get carried by iSCSI requests, and SCSI responses and status get carried by iSCSI responses. iSCSI also uses that same request response mechanism for its own iSCSI protocol mechanisms ๐ŸŒธ

for the rest of this document, the terms "initiator" and "target" mean "iSCSI initiator node" and "iSCSI target node", respectively (see Section 3.4.1 iSCSI Architecture Model), unless otherwise qualified โœจ

like similar protocols, the initiator and target split their communications into messages. this document calls these messages "iSCSI protocol data units" (iSCSI PDUs) ๐Ÿงš

for performance reasons, iSCSI allows a "phase-collapse". a command and its associated data may be shipped together from initiator to target, and data and responses may be shipped together from targets. efficiency is giving ๐Ÿ“

the iSCSI transfer direction is defined with respect to the initiator. outbound or outgoing transfers go from an initiator to a target, while inbound or incoming transfers go from a target to an initiator ๐Ÿ’œ

an iSCSI task is an iSCSI request for which a response is expected ๐ŸŽ€

in this document, "iSCSI request", "iSCSI command", request, and (unqualified) command all mean the same thing. also, unless otherwise specified, status, response, and numbered response mean the same thing too ๐Ÿซถ

3.2.1. Layers and Sessions

the following conceptual layering model is used to specify initiator and target actions, and how they relate to the PDUs that get sent and received ๐Ÿง 

a) the SCSI layer builds and receives SCSI CDBs (Command Descriptor Blocks), and passes them along with the remaining command execute parameters ([SAM2]) to and from

b) the iSCSI layer, which builds and receives iSCSI PDUs and relays them to and from one or more TCP connections. the group of connections forms an initiator-target "session" ๐ŸŒˆ

communication between the initiator and target happens over one or more TCP connections. those TCP connections carry control messages, SCSI commands, parameters, and data inside iSCSI Protocol Data Units (iSCSI PDUs) ๐Ÿ“ฆ the group of TCP connections that link an initiator with a target forms a session (loosely equivalent to a SCSI I_T nexus, see Section 3.4.2 SCSI Architecture Model). a session is defined by a session ID made up of an initiator part and a target part. TCP connections can be added to and removed from a session, and each connection within a session is identified by a connection ID (CID) ๐Ÿ”ฅ

across all connections within a session, an initiator sees one "target image". all target identifying elements, such as LUN, stay the same. a target also sees one "initiator image" across all connections in a session. initiator identifying elements, such as the Initiator Task Tag, are global across the session no matter which connection they're sent or received on ๐Ÿ‘‘

iSCSI targets and initiators MUST support at least one TCP connection and MAY support several connections in a session. for error recovery, targets and initiators that support a single active connection in a session SHOULD support two connections during recovery ๐Ÿ› ๏ธ ๐Ÿ’–

3.2.2. Ordering and iSCSI Numbering โœจ

okay so iSCSI runs three numbering systems at once ๐Ÿงš: Command, Status, and Data sequencing. each one has its own job, bestie ๐Ÿ’…

Command numbering is session-wide ๐ŸŽ€ and keeps commands in order even when they're spread across multiple connections. it can also act as a way to control command flow over a session.

Status numbering is per connection ๐ŸŒธ. it helps spot missing statuses and recover from errors, whether they're temporary glitches or permanent breaks.

Data sequencing is per command, or per part of a command (the R2T triggered sequence) ๐Ÿฆ‹. it catches missing data and missing R2T PDUs, including ones lost to header digest errors.

usually, fields inside the iSCSI PDUs carry these sequence numbers back and forth between the initiator and target ๐Ÿ’Œ. and when traffic only flows one way on a connection, iSCSI NOP-Out/In PDUs may be used to keep the command and status counters on the target and initiator in sync ๐Ÿซถ

the iSCSI session is basically the same thing as the SCSI I_T nexus โœจ, and it gives you ordered command delivery from the SCSI initiator to the SCSI target ๐Ÿ‘‘. if you want the deep design reasons behind the session model, and how it links up with SCSI command ordering, go read [CORD] ๐Ÿค“

3.2.2.1. Command Numbering and Acknowledging ๐Ÿ’–

iSCSI does ordered command delivery inside a session ๐ŸŽ€. every command (initiator-to-target PDU) on its way from the initiator to the target gets a number ๐Ÿ”ข

the target creates a task for every request the initiator sends ๐Ÿงš. a bunch of task management operations, including abort and reassign (see Section 10.5 Task Management Function Request), can be run on any iSCSI task ๐Ÿ’…

some iSCSI tasks are SCSI tasks, and lots of SCSI activity is tied to a SCSI task ([SAM2]). either way, the task is identified by the Initiator Task Tag for its whole life ๐Ÿท๏ธ

the command number rides along in the iSCSI PDU as CmdSN (Command Sequence Number) ๐ŸŽ€. it's session-wide, and every outgoing iSCSI PDU carries it. the iSCSI initiator hands out CmdSN values from a 32-bit unsigned counter (modulo 2**32) ๐Ÿ”ข. comparisons and arithmetic on CmdSN use Serial Number Arithmetic as defined in [RFC1982] where SERIAL_BITS = 32 ๐Ÿค“

commands meant for immediate delivery get marked with an immediate delivery flag ๐Ÿ”ฅ. they MUST also carry the current CmdSN. and CmdSN does not go up after an immediate command is sent ๐Ÿ™ˆ

command numbering kicks off with the first login request on the first connection of a session (the leading login on the leading connection) โœจ. after that, every non-immediate command gets its number bumped by 1 ๐Ÿ‘‘

immediate task management commands can reach the target before the tasks they're meant to act on ๐Ÿ˜ญ. but their CmdSN still marks where they sit in the command stream, so the initiator and target must make sure the task management commands act the way [SAM2] says. both commands and responses look like they were delivered in order, periodt ๐Ÿ’…

whenever an outgoing PDU's CmdSN isn't set by an explicit rule, it just carries the current value of the local CmdSN variable (more on that later in this section) ๐Ÿงš

how an implementation decides to mark a PDU for immediate delivery, or how iSCSI decides that on its own, is out of scope for this document ๐ŸŒˆ

the number of immediate commands isn't limited ๐Ÿฆ‹. their delivery for execution isn't acknowledged through the numbering scheme ๐Ÿ’–. immediate commands MAY be rejected by the iSCSI target layer if it's short on resources ๐Ÿ˜ฌ. an iSCSI target MUST be able to handle at least one immediate task management command and one immediate non-task-management iSCSI command per connection at any time ๐Ÿซถ

quick definition ๐ŸŽ€: in this document, "delivery for execution" means delivery to the SCSI execution engine, or to an iSCSI protocol specific execution engine (for example, text requests with public or private extension keys that involve an execution component) ๐Ÿง . apart from the commands marked for immediate delivery, the iSCSI target layer MUST deliver commands for execution in the order given by CmdSN ๐Ÿ‘‘. immediate commands can be handed off for execution as soon as they're detected โšก. iSCSI can hold back some commands from the SCSI target layer when a prior SCSI or iSCSI action requires it (for example, a CLEAR TASK SET Task Management request that shows up before all the commands it's supposed to act on) ๐Ÿ™ˆ

on any connection, the iSCSI initiator MUST send commands in increasing order of CmdSN, except for commands being retransmitted because of digest error recovery and connection recovery ๐Ÿ’…

for the numbering mechanism, the initiator and target each keep these three variables per session ๐Ÿงš:

  • CmdSN - the current command Sequence Number ๐ŸŽ€. it goes up by 1 on each command shipped, except for commands marked for immediate delivery. CmdSN always holds the number that gets assigned to the next Command PDU.
  • ExpCmdSN - the next command the target expects ๐ŸŒธ. the target acknowledges every command up to, but not including, this number. the initiator treats every command with a CmdSN below ExpCmdSN as acknowledged. the target iSCSI layer sets ExpCmdSN to the largest non-immediate CmdSN it can deliver for execution, plus 1 (no holes in the CmdSN sequence) โœจ
  • MaxCmdSN - the maximum number that can be shipped ๐Ÿ”ข. the queuing capacity of the receiving iSCSI layer is MaxCmdSN - ExpCmdSN + 1 ๐Ÿงบ

the initiator's ExpCmdSN and MaxCmdSN come from target-to-initiator PDU fields ๐Ÿ“ฌ. comparisons and arithmetic on ExpCmdSN and MaxCmdSN MUST use Serial Number Arithmetic as defined in [RFC1982] where SERIAL_BITS = 32 ๐Ÿค“

the target MUST NOT send a MaxCmdSN that is less than ExpCmdSN-1 ๐Ÿšซ. for non-immediate commands, the CmdSN field can be any value from ExpCmdSN to MaxCmdSN inclusive ๐ŸŽฏ. the target MUST silently ignore any non-immediate command outside of this range, or any non-immediate duplicates inside the range ๐Ÿซฅ. the CmdSN on immediate commands may fall outside the ExpCmdSN to MaxCmdSN range ๐Ÿฆ‹

for example, if the initiator has already sent a non-immediate command with CmdSN equal to MaxCmdSN, then the target window is closed ๐Ÿšช. for group task management commands sent as immediate commands, CmdSN shows the scope of the group action (for example, an ABORT TASK SET shows which commands get aborted) ๐Ÿงน

the initiator handles the MaxCmdSN and ExpCmdSN fields like this ๐Ÿ’…:

  • if the PDU MaxCmdSN is less than the PDU ExpCmdSN-1 (in the Serial Arithmetic sense), both get ignored ๐Ÿ™…โ€โ™€๏ธ
  • if the PDU MaxCmdSN is greater than the local MaxCmdSN (in the Serial Arithmetic sense), it updates the local MaxCmdSN. otherwise, it gets ignored ๐Ÿ”„
  • if the PDU ExpCmdSN is greater than the local ExpCmdSN (in the Serial Arithmetic sense), it updates the local ExpCmdSN. otherwise, it gets ignored ๐Ÿ”„

this sequence is needed because updates can show up out of order (for example, when the updates get sent on different TCP connections) ๐ŸŒ€

iSCSI initiators and targets MUST support the command numbering scheme ๐Ÿ’–.

a numbered iSCSI request keeps its CmdSN no matter how many times, or in what circumstances, it gets reissued (see Section 6.2.1 Usage of Retry) ๐Ÿ”. at the target, CmdSN only matters while the command hasn't created any state tied to its execution (execution state). after that, CmdSN stops mattering ๐Ÿ™ƒ. checking for the execution state (found by identifying the Initiator Task Tag) MUST come before any other action at the target ๐Ÿ”. if no execution state is found, ordering and delivery come next. if an execution state is found, delivery comes next ๐Ÿšš

if an initiator retries a command with CmdSN R on a connection while the session CmdSN value is Q, it MUST NOT advance CmdSN past R + 2**31 -1 ๐Ÿ›‘. the only exceptions are when the connection is no longer operational (meaning it has gone back to the FREE state, see Section 7.1.3 Standard Connection State Diagram for an Initiator), when the connection has been reinstated (see Section 5.3.4 Connection Reinstatement), or when a non-immediate command with CmdSN equal to or greater than Q was issued after the command retry on the same connection and the target has acknowledged its reception (see Section 9.4 Command Retry and Cleaning Old Command Instances) ๐ŸŽ€

a target MUST NOT issue a command response or Data-In PDU with status before it acknowledges the command ๐Ÿ™…โ€โ™€๏ธ. the acknowledgement can ride inside the response or Data-In PDU though ๐Ÿ’Œ

3.2.2.2. Response/Status Numbering and Acknowledging ๐ŸŒธ

responses going from the target back to the initiator are numbered too ๐Ÿฆ‹. the StatSN (Status Sequence Number) does this job ๐Ÿ”ข. StatSN is a counter kept per connection. the initiator uses ExpStatSN to acknowledge status ๐Ÿซถ. the status sequence number space is 32-bit unsigned-integers, and the arithmetic is regular mod(2**32) arithmetic ๐Ÿงฎ

status numbering starts with the Login response to the first Login request on the connection. the Login response includes an initial value for status numbering (any initial value is valid) โœจ

to make command recovery possible, the target MAY keep enough state information for data and status recovery after a connection failure ๐Ÿง . a target that does this can safely throw out all the state it kept for recovering a command once the status for that command (numbered StatSN) gets acknowledged through ExpStatSN ๐Ÿ—‘๏ธ

a big absolute difference between StatSN and ExpStatSN may mean the connection has failed ๐Ÿšจ. initiators MUST start recovery actions if the difference is greater than an implementation defined constant that MUST NOT exceed 2**31-1 ๐Ÿ˜ฌ

initiators and targets MUST support the response-numbering scheme ๐Ÿ’–.

3.2.2.3. Data Sequencing ๐Ÿงฉ

Data and R2T PDUs that are transferred as part of some command execution MUST be sequenced ๐ŸŽ€. the DataSN field handles data sequencing ๐Ÿ”ข. for input (read) data PDUs, DataSN starts at 0 for the first data PDU of an input command and goes up by 1 for each data PDU after that ๐Ÿ“–. for output data PDUs, DataSN starts at 0 for the first data PDU of a sequence (the initial unsolicited sequence, or any data PDU sequence sent to satisfy an R2T) and goes up by 1 for each data PDU after that โœ๏ธ

R2Ts are sequenced per command too ๐Ÿ”. for example, the first R2T has an R2TSN of 0 and goes up by 1 for each R2T after it ๐Ÿงฎ. for bidirectional commands, the target uses the DataSN/R2TSN to sequence Data-In and R2T PDUs in one continuous, undifferentiated sequence ๐ŸŒˆ

unlike command and status, data PDUs and R2Ts don't get acknowledged by a field in regular outgoing PDUs ๐Ÿ™ˆ. Data-In PDUs can be acknowledged on demand by a special form of the SNACK PDU ๐Ÿ’Œ. data and R2T PDUs are implicitly acknowledged by the status for the command ๐Ÿซถ. the DataSN/R2TSN field lets the initiator spot missing data or R2T PDUs ๐Ÿ•ต๏ธโ€โ™€๏ธ

for any read or bidirectional command, a target MUST issue fewer than 2**32 combined R2T and Data-In PDUs ๐Ÿ”ข. any output data sequence MUST contain fewer than 2**32 Data-Out PDUs ๐Ÿ’…

3.2.3. iSCSI Login โœจ

okay so the whole point of the iSCSI login is to get a TCP connection ready for iSCSI business ๐ŸŽ€. It handles authentication of both parties, negotiates the session's parameters, and marks the connection as part of an iSCSI session ๐Ÿ’….

A session is how a target keeps track of all the connections from one initiator that belong to the same I_T nexus ๐Ÿฆ‹. (For the deets on how a session relates to an I_T nexus, check Section 3.4.2 SCSI Architecture Model.)

Targets listen on a well-known TCP port (or some other TCP port) for incoming connections. The initiator starts the login by connecting to one of those ports ๐Ÿงš.

As part of login, the initiator and target SHOULD authenticate each other, and they MAY set a security association protocol for the session. There are lots of ways to do this and it's all negotiable ๐Ÿซถ.

To protect the TCP connection, an IPsec security association MAY be established before the Login request. For info on using IPsec security with iSCSI, see Chapter 8 and [RFC3723] ๐Ÿ”.

The iSCSI Login Phase runs on Login requests and responses. Once suitable authentication has happened and the operational parameters are set, the session moves to the Full Feature Phase and the initiator can start sending SCSI commands ๐Ÿš€. Whether and how a target decides to authorize an initiator is a security policy call that's beyond this document ๐Ÿ™ˆ. For a more detailed look at the Login Phase, see Chapter 5.

The login PDU includes the ISID part of the session ID (SSID) ๐Ÿ“. The target portal group that handles the login is implied by which connection endpoint you hit. For a new session, the TSIH is zero, and the target generates a TSIH as part of the response โœจ.

During session establishment, the target identifies the SCSI initiator port (the "I" in the "I_T nexus") with the value pair (InitiatorName, ISID). We'll get to InitiatorName later in this section ๐Ÿ‘‘. Any persistent state on the target tied to a SCSI initiator port (like persistent reservations) is identified using this pair. Any state tied to the SCSI target port (the "T" in the "I_T nexus") is identified externally by the TargetName and portal group tag (see Section 3.4.1 iSCSI Architecture Model). ISID is subject to reuse restrictions because it's used to identify persistent state (see Section 3.4.3 Consequences of the Model) ๐Ÿง .

Before the Full Feature Phase is established, only Login Request and Login Response PDUs are allowed. Login requests and responses MUST be used exclusively during Login. On any connection, the login phase MUST immediately follow TCP connection establishment, and a subsequent Login Phase MUST NOT occur before tearing down a connection ๐Ÿšซ.

A target that receives any PDU except a Login request before the Login phase has started MUST immediately terminate the connection on which the PDU was received. Once the Login phase has started, if the target receives any PDU except a Login request, it MUST send a Login reject (with Status "invalid during login") and then disconnect. If the initiator receives any PDU except a Login response, it MUST immediately terminate the connection ๐Ÿ’€.


3.2.4. iSCSI Full Feature Phase ๐ŸŒˆ

Once the initiator is authorized to do so, the iSCSI session is in the iSCSI Full Feature Phase ๐Ÿ‘‘. A session is in Full Feature Phase after successfully finishing the Login Phase on the first (leading) connection of a session. A connection is in Full Feature Phase if the session is in Full Feature Phase and the connection login has completed successfully.

An iSCSI connection is NOT in Full Feature Phase in either of these cases:

  • a) when it does not have an established transport connection,

OR

  • b) when it has a valid transport connection, but a successful login was not performed or the connection is currently logged out.

In a normal Full Feature Phase, the initiator may send SCSI commands and data to the various LUs on the target by wrapping them in iSCSI PDUs that go over the established iSCSI session ๐Ÿ’….

3.2.4.1. Command Connection Allegiance ๐ŸŽ€

For any iSCSI request issued over a TCP connection, the corresponding response and/or other related PDU(s) MUST be sent over the same connection. We call this "connection allegiance" ๐Ÿฆ‹. If the original connection fails before the command is completed, the connection allegiance of the command may be explicitly reassigned to a different transport connection, as described in detail in Section 6.2 Retry and Reassign in Recovery ๐Ÿ”„.

So, if an initiator issues a READ command, the target MUST send the requested data, if any, followed by the status to the initiator over the same TCP connection that was used to deliver the SCSI command. If an initiator issues a WRITE command, the initiator MUST send the data, if any, for that command over the same TCP connection that was used to deliver the SCSI command. The target MUST return Ready To Transfer (R2T), if any, and the status over the same TCP connection that was used to deliver the SCSI command. Retransmission requests (SNACK PDUs) and the data and status that they generate MUST also use the same connection ๐Ÿซถ.

However, consecutive commands that are part of a SCSI linked command-chain task (see [SAM2]) MAY use different connections ๐Ÿงš. Connection allegiance is strictly per-command and not per-task. During the iSCSI Full Feature Phase, the initiator and target MAY interleave unrelated SCSI commands, their SCSI Data, and responses over the session โœจ.

3.2.4.2. Data Transfer Overview ๐Ÿ“

Outgoing SCSI data (initiator to target user data or command parameters) is sent as either solicited data or unsolicited data. Solicited data is sent in response to R2T PDUs. Unsolicited data can be sent as part of an iSCSI command PDU ("immediate data") or in separate iSCSI data PDUs ๐Ÿ“ฆ.

Immediate data is assumed to originate at offset 0 in the initiator SCSI write-buffer (outgoing data buffer). All other Data PDUs have the buffer offset set explicitly in the PDU header ๐ŸŽฏ.

An initiator may send unsolicited data up to FirstBurstLength as immediate (up to the negotiated maximum PDU length), in a separate PDU sequence, or both. All subsequent data MUST be solicited. The maximum length of an individual data PDU or the immediate part of the first unsolicited burst MAY be negotiated at login ๐Ÿ”ฅ.

The maximum amount of unsolicited data that can be sent with a command is negotiated at login through the FirstBurstLength key. A target MAY separately enable immediate data (through the ImmediateData key) without enabling the more general (separate data PDUs) form of unsolicited data (through the InitialR2T key) ๐Ÿง.

Unsolicited data on write is meant to reduce the effect of latency on throughput (no R2T is needed to start sending data). Immediate data is also meant to reduce protocol overhead (both bandwidth and execution time) ๐ŸŒธ.

An iSCSI initiator MAY choose not to send unsolicited data, or to send only immediate data or FirstBurstLength bytes of unsolicited data with a command. If any non-immediate unsolicited data is sent, the total unsolicited data MUST be either FirstBurstLength, or all of the data if the total amount is less than FirstBurstLength ๐Ÿ“.

It's considered an error for an initiator to send unsolicited data PDUs to a target that operates in R2T mode (only solicited data is allowed) ๐Ÿ™…. It's also an error for an initiator to send more unsolicited data, whether immediate or as separate PDUs, than FirstBurstLength ๐Ÿ’€.

An initiator MUST honor an R2T data request for a valid outstanding command (i.e., one carrying a valid Initiator Task Tag) and deliver all the requested data, provided the command is supposed to deliver outgoing data and the R2T specifies data within the command bounds. The initiator action is unspecified for receiving an R2T request that specifies data, all or part, outside of the bounds of the command ๐Ÿคทโ€โ™€๏ธ.

A target SHOULD NOT silently discard data and then request retransmission through R2T. Initiators SHOULD NOT keep track of the data transferred to or from the target (scoreboarding) ๐Ÿ™ˆ. SCSI targets perform residual count calculation to check how much data was actually transferred to or from the device by a command. This may differ from the amount the initiator sent and/or received, for reasons such as retransmissions and errors. Read or bidirectional commands implicitly solicit the transmission of the entire amount of data covered by the command. SCSI data packets are matched to their corresponding SCSI commands by using tags specified in the protocol ๐ŸŽ€.

In addition, iSCSI initiators and targets MUST enforce some ordering rules. When unsolicited data is used, the order of the unsolicited data on each connection MUST match the order in which the commands on that connection are sent. Command and unsolicited data PDUs may be interleaved on a single connection as long as the ordering requirements of each are maintained. For example, command N+1 MAY be sent before the unsolicited Data-Out PDUs for command N, but the unsolicited Data-Out PDUs for command N MUST precede the unsolicited Data-Out PDUs of command N+1. A target that receives data out of order MAY terminate the session ๐Ÿšจ.

3.2.4.3. Tags and Integrity Checks ๐Ÿ”

Initiator tags for pending commands are unique initiator-wide for a session. Target tags are not strictly specified by the protocol. It's assumed that target tags are used by the target to tag (alone or in combination with the LUN) the solicited data. Target tags are generated by the target and "echoed" by the initiator. These mechanisms are designed to accomplish efficient data delivery, along with a large degree of control over the data flow ๐ŸŒŠ.

Because the Initiator Task Tag is used to identify a task during its execution, the iSCSI initiator and target MUST verify that all other fields used in task-related PDUs have values that are consistent with the values used at task instantiation, based on the Initiator Task Tag. For example, the LUN used in an R2T PDU MUST be the same as the one used in the SCSI command PDU that instantiated the task. Using inconsistent field values is considered a protocol error ๐Ÿšซ.

3.2.4.4. Task Management ๐Ÿงšโ€โ™€๏ธ

SCSI task management assumes that individual tasks and task groups can be aborted solely based on the task tags (for individual tasks) or the timing of the task management command (for task groups). It also assumes the task management action is executed synchronously, i.e., no message involving an aborted task will be seen by the SCSI initiator after receiving the task management response. In iSCSI, initiators and targets interact asynchronously over several connections. iSCSI specifies the protocol mechanism and implementation requirements needed to present a synchronous view while using an asynchronous infrastructure ๐Ÿ‘‘โœจ.

3.2.5. iSCSI Connection Termination ๐Ÿ’”โœจ

Okay so an iSCSI connection can end in one of two ways: a transport connection shutdown or a transport reset. Bestie, the reset is the dramatic one ๐Ÿ˜ญ It's assumed to be an exceptional event, not the everyday exit.

A graceful shutdown happens when TCP sends FINs ๐ŸŽ€. A graceful transport connection shutdown SHOULD only be initiated by either party when the connection is not in iSCSI Full Feature Phase. So the vibe is: say goodbye politely outside of Full Feature Phase ๐Ÿ’….

Targets get a little wiggle room here. A target MAY terminate a Full Feature Phase connection on internal exception events ๐Ÿซฃ, but it SHOULD announce the fact through an Asynchronous Message PDU. No ghosting, just announce it ๐Ÿ“ฃ. And heads up: connection termination with outstanding commands may require recovery actions ๐Ÿ™ˆ

If a connection gets terminated while it's in Full Feature Phase, connection cleanup (see section 7) is required before recovery starts ๐Ÿงน. Doing the cleanup first means the initiator and target won't receive stale PDUs after recovery. Main character energy is cleaning up before the sequel ๐Ÿฆ‹

3.2.6. iSCSI Names ๐Ÿท๏ธ

Both targets and initiators need names so they can be identified ๐Ÿชช. Names also let iSCSI storage resources be managed no matter where they live (their address). Fun fact: an iSCSI node name is also the SCSI device name of an iSCSI device ๐Ÿงš. The iSCSI name of a SCSI device is the principal object used in authentication, meaning targets authenticate initiators and initiators authenticate targets with it ๐Ÿ”. It's also how you identify and manage storage resources ๐Ÿ‘‘

iSCSI names have to be unique within the operational domain of the end user. But an IP network's operational domain can be worldwide, so the name formats are built to be worldwide unique ๐ŸŒ. To help naming authorities build worldwide unique names, iSCSI gives them two name formats for different kinds of naming authorities ๐Ÿ’œ

Names are tied to iSCSI nodes, not to network adapter cards ๐Ÿƒ. This way, swapping out a NIC doesn't mean reconfiguring all the SCSI and iSCSI resource allocation info. Iconic, honestly ๐Ÿ˜ค

Some SCSI commands need protocol-specific identifiers sent inside SCSI CDBs. For the definition of the SCSI port name/identifier for iSCSI ports, check out Section 3.4.2 SCSI Architecture Model ๐Ÿ“š

An initiator can discover the iSCSI Target Names it has access to, plus their addresses, using the SendTargets text request, or other techniques discussed in [RFC3721] ๐Ÿ”

3.2.6.1. iSCSI Name Properties ๐ŸŒธ

Each iSCSI node, whether it's an initiator or a target, MUST have an iSCSI name. No exceptions, no cap ๐Ÿงข

Initiators and targets MUST support receiving iSCSI names up to the maximum length of 223 bytes ๐Ÿ“

The initiator MUST present both its iSCSI Initiator Name and the iSCSI Target Name it wants to connect to in the first login request of a new session or connection ๐ŸŽŸ๏ธ. The only exception is a discovery session (see Section 2.3 iSCSI Session Types). For that one, the iSCSI Initiator Name is still required, but the iSCSI Target Name MAY be omitted ๐Ÿซถ

Now the properties. iSCSI names have these:

  • a) iSCSI names are globally unique. No two initiators or targets can have the same name ๐ŸŒŽ
  • b) iSCSI names are permanent. An iSCSI initiator node or target node keeps the same name for its entire lifetime ๐Ÿ’Ž
  • c) iSCSI names do not imply a location or address. An iSCSI initiator or target can move, or have multiple addresses. A change of address does not imply a change of name ๐Ÿ 
  • d) iSCSI names do not rely on a central name broker. The naming authority is distributed ๐Ÿ•ธ๏ธ
  • e) iSCSI names support integration with existing unique naming schemes ๐Ÿค
  • f) iSCSI names rely on existing naming authorities. iSCSI does not create any new naming authority ๐Ÿ™…โ€โ™€๏ธ

The encoding of an iSCSI name has these properties:

  • a) iSCSI names use the same encoding method regardless of the underlying protocols ๐ŸŽ€
  • b) iSCSI names are relatively simple to compare. The algorithm for comparing two iSCSI names for equivalence does not rely on an external server ๐Ÿงฎ
  • c) iSCSI names are made only of displayable characters. They allow international character sets but are not case sensitive. No whitespace characters are used in iSCSI names โœจ
  • d) iSCSI names may be transported using both binary and ASCII-based protocols ๐Ÿฆ‹

An iSCSI name really names a logical software entity. It isn't tied to a port or any hardware that could get swapped out ๐Ÿ”ง. For example, an initiator name should name the iSCSI initiator node, not a particular NIC or HBA. When there are multiple NICs, they should generally all present the same iSCSI initiator name to the targets, since they're just paths to the same SCSI layer ๐Ÿ›ค๏ธ. On most operating systems, the named entity is the operating system image ๐Ÿ–ฅ๏ธ

Targets are the same: a target name shouldn't be tied to hardware interfaces that can change ๐Ÿ™…โ€โ™€๏ธ. A target name should identify the logical target and must be the same no matter which physical portion is being addressed. That's how initiators figure out that the two targets they discovered are really two paths to the same target ๐Ÿงฉ

The iSCSI name is designed to meet the functional requirements for Uniform Resource Names (URN) [RFC1737] ๐Ÿ“›. So the name needs global scope, has to be independent of address or location, and has to be persistent and globally unique. Names must be extensible and scalable with naming authorities. The name encoding should be readable by both humans and machines ๐Ÿค“. Check [RFC1737] for more requirements ๐Ÿ’…

3.2.6.2. iSCSI Name Encoding ๐Ÿ”ค

An iSCSI name MUST be a UTF-8 encoding of a string of Unicode characters with these properties:

  • It is in Normalization Form C (see "Unicode Normalization Forms" [UNICODE]).
  • It only contains characters allowed by the output of the iSCSI stringprep template (described in [RFC3722]).
  • The following characters are used for formatting iSCSI names:
    • dash ('-'=U+002d)
    • dot ('.'=U+002e)
    • colon (':'=U+003a)
  • The UTF-8 encoding of the name is not larger than 223 bytes.

The stringprep process is described in [RFC3454], and iSCSI's use of it is described in [RFC3722] ๐Ÿ“–. Stringprep is a method the Internationalized Domain Name (IDN) working group designed to turn human-typed strings into something you can compare as opaque strings ๐Ÿ” . Strings MUST NOT include punctuation, spacing, diacritical marks, or other characters that could get in the way of readability ๐Ÿšซ. Stringprep also converts strings into equivalent lower-case strings ๐Ÿ”ก

The stringprep process doesn't need to be implemented if names are only generated using numeric and lower-case alphabetic characters (any character set) ๐Ÿ™Œ

Once iSCSI names encoded in UTF-8 are "normalized", they can be safely compared byte-for-byte ๐Ÿฅฐ

3.2.6.3. iSCSI Name Structure ๐Ÿงฑ

An iSCSI name has two parts: a type designator followed by a unique name string ๐ŸŽ€

The iSCSI name doesn't define any new naming authorities. Instead, it supports two existing ways of designating them: an iSCSI-Qualified Name, which uses domain names to identify a naming authority, and the EUI format, where the IEEE Registration Authority helps form worldwide unique names (EUI-64 format) ๐ŸŒ

The type designator strings currently defined are:

  • iqn. - iSCSI Qualified name
  • eui. - Remainder of the string is an IEEE EUI-64 identifier, in ASCII-encoded hexadecimal.

These two naming authority designators were considered sufficient when this document was written. Additional naming type designators for iSCSI may be considered by the IETF and detailed in separate RFCs ๐Ÿ”ฎ

3.2.6.3.1. Type "iqn." (iSCSI Qualified Name) ๐Ÿ“

Any organization that owns a domain name can use this iSCSI name type ๐Ÿข. It's super useful when an end user or service provider wants to assign iSCSI names for targets and/or initiators ๐Ÿ’–

To generate names of this type, the person or organization generating the name must own a registered domain name. The domain name doesn't have to be active, and it doesn't have to resolve to an address. It just needs to be reserved so nobody else can generate iSCSI names with the same domain name ๐Ÿ”’

Here's the tea: a domain name can expire, get acquired by another entity, or even be used by both owners to generate iSCSI names. So the domain name also has to be qualified by a date during which the naming authority owned it. That's why there's a date code in the iqn. format ๐Ÿ“…

The iSCSI qualified name string consists of:

  • The string iqn., used to tell these names apart from eui. formatted names.
  • A date code in yyyy-mm format. This date MUST be a date during which the naming authority owned the domain name used in this format, and SHOULD be the first month the naming authority owned the domain name, at 00:01 GMT on the first day of that month. The date code uses the Gregorian calendar. All four digits of the year must be present. Both digits of the month must be present, with January == "01" and December == "12". The dash must be included ๐Ÿ—“๏ธ
  • A dot .
  • The reversed domain name of the naming authority (person or organization) creating this iSCSI name.
  • An optional string, prefixed by a colon (:), within the character set and length boundaries that the domain name owner thinks are appropriate. It can contain product types, serial numbers, host identifiers, or software keys (for example, it may include colons to separate organization boundaries). Apart from the colon prefix, the domain name owner can assign everything after the reversed domain name however they like. The entity that is the naming authority is responsible for making sure the iSCSI names it assigns are worldwide unique ๐Ÿซก. For example, "Example Storage Arrays, Inc.", might own the domain name "example.com" ๐Ÿ—๏ธ

Here are some iSCSI qualified names that "EXAMPLE Storage Arrays, Inc." might generate ๐ŸŽ‰:

                   Naming     String defined by
      Type  Date    Auth      "example.com" naming authority
     +--++-----+ +---------+ +--------------------------------+
     |  ||     | |         | |                                |

     iqn.2001-04.com.example:storage:diskarrays-sn-a8675309
     iqn.2001-04.com.example
     iqn.2001-04.com.example:storage.tape1.sys1.xyz
     iqn.2001-04.com.example:storage.disk2.sys1.xyz

3.2.6.3.2. Type "eui." (IEEE EUI-64 format) ๐ŸŽ€

Okay bestie, so the IEEE Registration Authority runs a service that hands out globally unique identifiers [EUI] ๐ŸŒ The EUI-64 format is what other network protocols use to build a global identifier. Fibre Channel, for example, has a method for encoding it into a WorldWideName, which is so iconic ๐Ÿ’… If you want to register for EUI identifiers, check out [OUI] โœจ

The format is "eui." followed by an EUI-64 identifier (16 ASCII-encoded hexadecimal digits) ๐Ÿ’–

Example iSCSI name:

        Type  EUI-64 identifier (ASCII-encoded hexadecimal)
        +--++--------------+
        |  ||              |
        eui.02004567A425678D

The IEEE EUI-64 iSCSI name format might be used when a manufacturer is already registered with the IEEE Registration Authority and uses EUI-64 formatted worldwide unique names for its products ๐Ÿญ๐Ÿฆ‹

More examples of name construction are discussed in [RFC3721] ๐Ÿ“š

3.2.7. Persistent State ๐ŸŒธ

iSCSI does not require any persistent state maintenance across sessions. Lowkey a relief, right? ๐Ÿ˜Œ However, in some cases, SCSI requires persistent identification of the SCSI initiator port name (See Section 3.4.2 SCSI Architecture Model and Section 3.4.3 Consequences of the Model) ๐Ÿง 

iSCSI sessions do not persist through power cycles and boot operations ๐Ÿ”Œ๐Ÿ’€

All iSCSI session and connection parameters are re-initialized upon session and connection creation. Fresh start every time, no cap โœจ

Commands persist beyond connection termination if the session persists and command recovery within the session is supported. However, when a connection is dropped, command execution, as perceived by iSCSI (i.e., involving iSCSI protocol exchanges for the affected task), is suspended until a new allegiance is established by the 'task reassign' task management function. (See Section 10.5 Task Management Function Request.) ๐Ÿซถ

3.2.8. Message Synchronization and Steering ๐Ÿงš

iSCSI presents a mapping of the SCSI protocol onto TCP. This encapsulation is accomplished by sending iSCSI PDUs of varying lengths. Unfortunately, TCP does not have a built-in mechanism for signaling message boundaries at the TCP layer ๐Ÿ˜ญ iSCSI gets around this by placing the message length in the iSCSI message header. This serves to delineate the end of the current message as well as the beginning of the next message. Main character energy ๐Ÿ‘‘

In situations where IP packets are delivered in order from the network, iSCSI message framing is not an issue and messages are processed one after the other. In the presence of IP packet reordering (i.e., frames being dropped), legacy TCP implementations store the "out of order" TCP segments in temporary buffers until the missing TCP segments arrive, upon which the data must be copied to the application buffers. In iSCSI, it is desirable to steer the SCSI data within these out of order TCP segments into the pre-allocated SCSI buffers rather than store them in temporary buffers. This decreases the need for dedicated reassembly buffers as well as the latency and bandwidth related to extra copies ๐Ÿ”ฅ

Relying solely on the "message length" information from the iSCSI message header may make it impossible to find iSCSI message boundaries in subsequent TCP segments due to the loss of a TCP segment that contains the iSCSI message length. The missing TCP segment(s) must be received before any of the following segments can be steered to the correct SCSI buffers (due to the inability to determine the iSCSI message boundaries). Since these segments cannot be steered to the correct location, they must be saved in temporary buffers that must then be copied to the SCSI buffers ๐Ÿ™ˆ

Different schemes can be used to recover synchronization. To make these schemes work, iSCSI implementations have to make sure that the appropriate protocol layers are provided with enough information to implement a synchronization and/or data steering mechanism. One of these schemes is detailed in Appendix A. - Sync and Steering with Fixed Interval Markers -. ๐Ÿ“–

The Fixed Interval Markers (FIM) scheme works by inserting markers in the payload stream at fixed intervals that contain the offset for the start of the next iSCSI PDU ๐Ÿ“

Under normal circumstances (no PDU loss or data reception out of order), iSCSI data steering can be accomplished by using the identifying tag and the data offset fields in the iSCSI header in addition to the TCP sequence number from the TCP header. The identifying tag helps associate the PDU with a SCSI buffer address while the data offset and TCP sequence number are used to determine the offset within the buffer ๐ŸŽฏ

When the part of the TCP data stream containing an iSCSI PDU header is delayed or lost, markers may be used to minimize the damage as follows:

  • Markers indicate where the next iSCSI PDU starts and enable continued processing when iSCSI headers have to be dropped due to data errors discovered at the iSCSI level (e.g., iSCSI header CRC errors). ๐Ÿฉน
  • Markers help minimize the amount of data that has to be kept by the TCP/iSCSI layer while waiting for a late TCP packet arrival or recovery, because later they might help find iSCSI PDU headers and use the information contained in those to steer data to SCSI buffers. ๐Ÿ’œ

3.2.8.1. Sync/Steering and iSCSI PDU Length ๐Ÿ“

When a large iSCSI message is sent, the TCP segment(s) that contain the iSCSI header may be lost. The remaining TCP segment(s), up to the next iSCSI message, must be buffered (in temporary buffers) because the iSCSI header that indicates to which SCSI buffers the data are to be steered was lost ๐Ÿ˜ฉ To minimize the amount of buffering, it is recommended that the iSCSI PDU length be restricted to a small value (perhaps a few TCP segments in length). During login, each end of the iSCSI session specifies the maximum iSCSI PDU length it will accept ๐Ÿซถ

3.3. iSCSI Session Types ๐ŸŒˆ

iSCSI defines two types of sessions:

a) Normal operational session - an unrestricted session. b) Discovery-session - a session only opened for target discovery. The target MUST ONLY accept text requests with the SendTargets key and a logout request with the reason "close the session". All other requests MUST be rejected.

The session type is defined during login with the key=value parameter in the login command ๐Ÿ”‘

3.4. SCSI to iSCSI Concepts Mapping Model โœจ

The following diagram shows an example of how multiple iSCSI Nodes (targets in this case) can coexist within the same Network Entity and can share Network Portals (IP addresses and TCP ports). Other more complex configurations are also possible. For detailed descriptions of the components of these diagrams, see Section 3.4.1 iSCSI Architecture Model ๐Ÿงฉ

                  +-----------------------------------+
                  |  Network Entity (iSCSI Client)    |
                  |                                   |
                  |         +-------------+           |
                  |         | iSCSI Node  |           |
                  |         | (Initiator) |           |
                  |         +-------------+           |
                  |            |       |              |
                  | +--------------+ +--------------+ |
                  | |Network Portal| |Network Portal| |
                  | |   10.1.30.4  | |   10.1.40.6  | |
                  +-+--------------+-+--------------+-+
                           |               |
                           |  IP Networks  |
                           |               |
                  +-+--------------+-+--------------+-+
                  | |Network Portal| |Network Portal| |
                  | |  10.1.30.21  | |   10.1.40.3  | |
                  | | TCP Port 3260| | TCP Port 3260| |
                  | +--------------+ +--------------+ |
                  |        |               |          |
                  |        -----------------          |
                  |           |         |             |
                  |  +-------------+ +--------------+ |
                  |  | iSCSI Node  | | iSCSI Node   | |
                  |  |  (Target)   | |  (Target)    | |
                  |  +-------------+ +--------------+ |
                  |                                   |
                  |   Network Entity (iSCSI Server)   |
                  +-----------------------------------+

3.4.1. iSCSI Architecture Model โœจ

okay so this part is the bit of the iSCSI architecture model that matters most for how iSCSI lines up with the SCSI Architecture Model ๐Ÿง ๐Ÿ’–

a) Network Entity ๐ŸŒ - this is a device or gateway you can reach from the IP network. A Network Entity must have one or more Network Portals (see item d), and each portal can be used by some of the iSCSI Nodes (see item b) inside that Network Entity to get onto the IP network.

b) iSCSI Node ๐Ÿฆ‹ - this is one single iSCSI initiator or iSCSI target. There are one or more iSCSI Nodes inside a Network Entity. The iSCSI Node can be reached through one or more Network Portals (see item d). Each iSCSI Node is identified by its iSCSI Name (see Section 3.2.6 iSCSI Names and Chapter 12). Keeping the iSCSI Name separate from the addresses the node uses is the whole trick, because it lets several iSCSI nodes share the same addresses and lets one iSCSI node use several addresses ๐ŸŽ€โœจ

c) An alias string can also be attached to an iSCSI Node ๐Ÿท๏ธ It lets an organization pair a friendly, human-readable string with the iSCSI Name. BUT the alias is NOT a replacement for the iSCSI Name, so don't mix them up bestie ๐Ÿ™ˆ

d) Network Portal ๐Ÿšช - this is a piece of a Network Entity that has a TCP/IP network address. An iSCSI Node inside that Network Entity may use it for the connection(s) within one of its iSCSI sessions. On an initiator, it's identified by its IP address. On a target, it's identified by its IP address plus its listening TCP port ๐Ÿ’…

e) Portal Groups ๐Ÿงš - iSCSI supports multiple connections inside the same session, and some implementations can combine connections in a session across several Network Portals. A Portal Group is a set of Network Portals inside an iSCSI Node that together can coordinate a session with connections spanning those portals. Not every Network Portal in a Portal Group has to take part in every session that goes through that Portal Group. One or more Portal Groups may provide access to an iSCSI Node. Every Network Portal, as used by a given iSCSI Node, belongs to exactly one portal group inside that node. Portal Groups are identified inside an iSCSI Node by a portal group tag, which is a simple unsigned integer between 0 and 65535 (see Section 12.3 SendTargets). All Network Portals sharing the same portal group tag within a given iSCSI Node are in the same Portal Group ๐Ÿ’œ

Both iSCSI Initiators and iSCSI Targets have portal groups, but only the iSCSI Target Portal Groups get used directly in the iSCSI protocol (for example, in SendTargets). For references to the initiator Portal Groups, see Section 9.1.1 Conservative Reuse of ISIDs ๐ŸŒธ

f) Portals inside a Portal Group should support similar session parameters, because they might end up taking part in a shared session ๐Ÿซถ

Here's a diagram of one such setup on a target, and how a session that shares Network Portals within a Portal Group can get established ๐Ÿ‘‘

     ----------------------------IP Network---------------------
            |               |                    |
       +----|---------------|-----+         +----|---------+
       | +---------+  +---------+ |         | +---------+  |
       | | Network |  | Network | |         | | Network |  |
       | | Portal  |  | Portal  | |         | | Portal  |  |
       | +--|------+  +---------+ |         | +---------+  |
       |    |               |     |         |    |         |
       |    |    Portal     |     |         |    | Portal  |
       |    |    Group 1    |     |         |    | Group 2 |
       +--------------------------+         +--------------+
            |               |                    |
   +--------|---------------|--------------------|--------------------+
   |        |               |                    |                    |
   |  +----------------------------+  +-----------------------------+ |
   |  | iSCSI Session (Target side)|  | iSCSI Session (Target side) | |
   |  |                            |  |                             | |
   |  |       (TSIH = 56)          |  |       (TSIH = 48)           | |
   |  +----------------------------+  +-----------------------------+ |
   |                                                                  |
   |                     iSCSI Target Node                            |
   |             (within Network Entity, not shown)                   |
   +------------------------------------------------------------------+

3.4.2. SCSI Architecture Model ๐Ÿ’…

okay this section explains how the SCSI Architecture Model [SAM2] lines up with the SCSI device, SCSI port and I_T nexus constructs, and with the iSCSI constructs from Section 3.4.1 iSCSI Architecture Model ๐ŸŽ€

This relationship creates some implementation requirements so you can actually conform to the SAM2 model and the other SCSI operational functions. Those requirements get spelled out in Section 3.4.3 Consequences of the Model ๐Ÿงš

Here's the list of how SCSI architectural elements map onto iSCSI ๐Ÿ’–

a) SCSI Device ๐Ÿค– - this is the SAM2 term for an entity that holds one or more SCSI ports connected to a service delivery subsystem and supports a SCSI application protocol. For example, a SCSI Initiator Device has one or more SCSI Initiator Ports and zero or more application clients. A SCSI Target Device has one or more SCSI Target Ports and one or more logical units. For iSCSI, the SCSI Device is the component inside an iSCSI Node that provides the SCSI functionality. So there can be ONE SCSI Device, at most, inside an iSCSI Node. Access to the SCSI Device can only be achieved in an iSCSI normal operational session (see Section 3.3 iSCSI Session Types). The SCSI Device Name is defined to be the iSCSI Name of the node, and it MUST be used in the iSCSI protocol ๐Ÿ‘‘

b) SCSI Port ๐Ÿ”Œ - this is the SAM2 term for an entity inside a SCSI Device that provides the SCSI functionality needed to interface with a service delivery subsystem or transport. For iSCSI, the definitions of SCSI Initiator Port and SCSI Target Port are different ๐ŸŒˆ

SCSI Initiator Port: this maps to one endpoint of an iSCSI normal operational session (see Section 3.3 iSCSI Session Types). An iSCSI normal operational session gets negotiated through the login process between an iSCSI initiator node and an iSCSI target node. When that process finishes successfully, a SCSI Initiator Port gets created inside the SCSI Initiator Device. The SCSI Initiator Port Name and the SCSI Initiator Port Identifier are both defined to be the iSCSI Initiator Name together with (a) a label that marks it as an initiator port name/identifier and (b) the ISID portion of the session identifier ๐Ÿ’œ

SCSI Target Port: this maps to an iSCSI Target Portal Group. The SCSI Target Port Name and the SCSI Target Port Identifier are both defined to be the iSCSI Target Name together with (a) a label that marks it as a target port name/identifier and (b) the portal group tag ๐Ÿ“

The SCSI Port Name MUST be used in iSCSI. When it shows up in SCSI parameter data, the SCSI port name MUST be encoded like this ๐Ÿ“

  • The iSCSI Name in UTF-8 format, followed by
  • a comma separator (1 byte), followed by
  • the ASCII character 'i' (for SCSI Initiator Port) or the ASCII character 't' (for SCSI Target Port) (1 byte), followed by
  • a comma separator (1 byte), followed by
  • a text encoding as a hex-constant (see Section 5.1 Text Format) of the ISID (for SCSI initiator port) or the portal group tag (for SCSI target port), including the initial 0X or 0x and the terminating null (15 bytes).

The ASCII character 'i' or 't' is the label that says whether this port is a SCSI Initiator Port or a SCSI Target Port ๐Ÿฆ‹

c) I_T nexus ๐Ÿ”— - this is a relationship between a SCSI Initiator Port and a SCSI Target Port, according to [SAM2]. For iSCSI, that relationship is a session, defined as a relationship between an iSCSI Initiator's end of the session (the SCSI Initiator Port) and the iSCSI Target's Portal Group. The I_T nexus can be identified by combining the SCSI port names, or by the iSCSI session identifier SSID. iSCSI defines the I_T nexus identifier as the tuple (iSCSI Initiator Name + 'i' + ISID, iSCSI Target Name + 't' + Portal Group Tag) โœจ

NOTE: the I_T nexus identifier is NOT equal to the session identifier (SSID). Don't mix those up bestie ๐Ÿ™ˆ


3.4.3. Consequences of the Model ๐ŸŒŸ

this section covers the implementation and behavioral requirements that fall out of mapping SCSI constructs onto the iSCSI constructs we just went through ๐Ÿง  Between a given SCSI initiator port and a given SCSI target port, only ONE I_T nexus (session) can exist. [SAM2] doesn't allow more than one nexus relationship (a parallel nexus). So at any given time, only one session can exist between a given iSCSI initiator node and an iSCSI target node with the same session identifier (SSID) ๐Ÿ’–

These assumptions lead to the following conclusions and requirements ๐ŸŽ€

ISID RULE: between a given iSCSI Initiator and iSCSI Target Portal Group (SCSI target port), there can only be one session with a given value for ISID that identifies the SCSI initiator port. See Section 10.12.5 ISID ๐Ÿ’…

The structure of the ISID, which includes a naming authority component (see Section 10.12.5 ISID and [RFC3721]), gives you a mechanism to help comply with the ISID rule. (See Section 9.1.1 Conservative Reuse of ISIDs.) The iSCSI Initiator Node should manage the assignment of ISIDs before session initiation ๐ŸŒธ The "ISID RULE" does NOT preclude using the same ISID from the same iSCSI Initiator with different Target Portal Groups on the same iSCSI target, or on other iSCSI targets (see Section 9.1.1 Conservative Reuse of ISIDs). Allowing that would be like one SCSI Initiator Port having nexus relationships with several SCSI target ports on the same SCSI target device, or with SCSI target ports on other SCSI target devices ๐Ÿฆ‹ It's also possible to have several sessions with different ISIDs going to the same Target Portal Group. Each of those sessions counts as being with a different initiator, even when they all come from the same initiator device ๐Ÿงš The same ISID can be used by a different iSCSI initiator, because it's the iSCSI Name together with the ISID that identifies the SCSI Initiator Port ๐Ÿ‘‘

NOTE: because of the ISID RULE and the spec for the I_T nexus identifier, two nexus with the same identifier should never exist at the same time ๐Ÿ™ˆ

TSIH RULE: the iSCSI Target selects a non-zero value for the TSIH at session creation (when an initiator presents a 0 value at Login). Once it's selected, the same TSIH value MUST be used whenever the initiator or target refers to the session and a TSIH is required ๐ŸŒˆ๐Ÿ’œ

3.4.3.1. I_T Nexus State โœจ

okay so some nexus relationships carry explicit state, like initiator-specific mode pages ๐Ÿ“, and the device server [SAM2] needs to keep that state safe in a logical unit even when the iSCSI layer glitches (think session failures) ๐Ÿ˜ญ. To get it back, the iSCSI initiator should re-login to the same Target Portal Group using the previous ISID ๐Ÿซถ. That's session recovery, the thing Chapter 6 walks through ๐Ÿ’…. Why? The SCSI initiator port identifier and the SCSI target port identifier (or relative target port) together form the datum the SCSI logical unit device server uses to identify the I_T nexus. so it's literally the whole ID combo ๐Ÿ‘‘


3.5. Request/Response Summary ๐ŸŽ€

this section is the cheat sheet ๐Ÿ“ listing and briefly describing all the iSCSI PDU types (requests and responses).

every iSCSI PDU is built from one or more header segments (basic and auxiliary) and zero or one data segments ๐Ÿงš. the header group and the data segment may each be followed by a CRC (digest) ๐Ÿ”.

the basic header segment has a fixed length of 48 bytes ๐Ÿ’œ.

3.5.1. Request/Response Types Carrying SCSI Payload ๐ŸŒธ

3.5.1.1. SCSI-Command

this request carries the SCSI CDB plus all the other IN arguments for the SCSI execute command procedure call (see [SAM2]) ๐Ÿฆ‹. that includes task attributes, the Expected Data Transfer Length for one or both directions (the latter for bidirectional commands), and the Task Tag (as part of the I_T_L_x nexus). the initiator and target derive the I_T_L nexus from the LUN field in the request, and the I_T nexus is implicit in the session identification ๐Ÿซถ

the SCSI-Command PDU also carries the stuff iSCSI needs to run properly: the command sequence number (CmdSN) for the session and the expected status number (ExpStatSN) for the connection โœจ

all or part of the SCSI output (write) data for the command can ride along inside the SCSI-Command PDU as a data segment ๐Ÿš€

3.5.1.2. SCSI-Response

the SCSI-Response carries all the OUT arguments of the SCSI execute-command procedure call (see [SAM2]) and the procedure call's return value ๐Ÿ’…

it also holds the residual counts from the operation (if there are any), an indication of whether those counts are an overflow or an underflow, and either the SCSI status (when the status is valid) or a response code (a non-zero return value for the execute-command procedure call) when the status isn't valid ๐Ÿ™ˆ

for a valid status saying the command was processed but threw an exception (like a SCSI CHECK CONDITION) ๐Ÿ’€, the data segment holds the matching sense data. Autosense ([SAM2]) is REQUIRED by iSCSI ๐Ÿ”ฅ

some data segment content may also go with a non-zero response code ๐ŸŒˆ

the SCSI-Response PDU also carries the iSCSI stuff that keeps things running smoothly:

  • the number of Data-In PDUs that a target has sent (so the initiator can check that they all arrived) ๐Ÿ“
  • StatSN, the Status Sequence Number on this connection
  • ExpCmdSN, the next Expected Command Sequence Number at the target ๐Ÿ‘‘
  • MaxCmdSN, the maximum CmdSN the target will accept from this initiator ๐Ÿ’–

3.5.1.3 Task Management Function Request

the Task Management function request is how an initiator explicitly controls one or more SCSI Tasks or iSCSI functions ๐ŸŽฏ. the PDU carries a function identifier (which task management function to perform) plus enough info to unequivocally identify the task or task set to act on, even if that task hasn't arrived yet or got tossed because of an error ๐Ÿซ 

the referenced tag identifies an individual task whenever the function targets one individual task ๐Ÿฆ‹

the I_T_L nexus is what identifies task sets. in iSCSI, the I_T_L nexus is identified by the LUN and the session identification (the session identifies an I_T nexus) ๐Ÿงš

for task sets, the CmdSN of the Task Management function request helps pick out the tasks to act on: all tasks associated with a LUN that have a CmdSN preceding the Task Management function request's CmdSN ๐ŸŒธ

for a Task Management function, the target handles coordinating the responses to the affected tasks and the Task Management function response โœจ

3.5.1.4. Task Management Function Response

the Task Management function response tells you the function completed for a Task Management function request, including how it was completed (response and qualifier) and extra info for failure responses ๐Ÿ’…

once the Task Management response says the function is complete, the initiator won't get any more responses from the affected tasks ๐Ÿ›‘

3.5.1.5. SCSI Data-Out and SCSI Data-In

SCSI Data-Out and SCSI Data-In are the main vehicles for moving SCSI data payload between initiator and target ๐Ÿšš. the payload gets tied to a specific SCSI command through the Initiator Task Tag ๐Ÿท๏ธ. for the target's convenience, outgoing solicited data also carries a Target Transfer Tag (copied from R2T) and the LUN ๐ŸŽ€. each PDU includes the payload length and the data offset relative to the buffer address in the SCSI execute command procedure call ๐Ÿ“

in each direction, the data transfer gets split into "sequences" ๐Ÿงฉ. an end-of-sequence is flagged with the F bit ๐Ÿ

an outgoing sequence is either unsolicited (only the first sequence can be unsolicited) or made up of all the Data-Out PDUs sent in response to an R2T ๐Ÿ’Œ

input sequences are built to allow direction switching for bidirectional commands ๐Ÿ”„

for input, the target may ask for positive acknowledgement of the input data. this only works in sessions that support error recovery, and it's done with the A bit in the SCSI Data-In PDU header ๐Ÿ™‹โ€โ™€๏ธ

Data-In and Data-Out PDUs also carry the DataSN, so the initiator and target can spot missing PDUs (the ones discarded because of an error) ๐Ÿ”

Data-In PDUs also carry StatSN ๐Ÿ“Š

to get a SCSI command done with as few messages as possible, the last SCSI Data-In PDU for a command may also carry the status, as long as the status means termination with no exceptions (no sense or response involved) ๐Ÿ†

3.5.1.6. Ready To Transfer (R2T)

R2T is how the SCSI target "requests" output data from the initiator ๐Ÿ™Œ. it tells the initiator the offset of the requested data relative to the buffer address from the execute command procedure call, plus the length of the solicited data ๐Ÿ“

so the target can match the resulting Data-Out with an R2T, the R2T carries a Target Transfer Tag. the initiator copies that tag into the solicited SCSI Data-Out PDUs ๐Ÿช„. there are no protocol-specific requirements for what these tag values are, but it's assumed that together with the LUN, they'll let the target associate data with an R2T ๐Ÿซถ

R2T also carries the info iSCSI needs to run properly, such as:

  • R2TSN (so an initiator can detect a missing R2T) ๐Ÿ•ต๏ธโ€โ™€๏ธ
  • StatSN
  • ExpCmdSN
  • MaxCmdSN

3.5.2. Requests/Responses carrying SCSI and iSCSI Payload ๐Ÿฆ‹

3.5.2.1. Asynchronous Message

Asynchronous Messages carry SCSI asynchronous events (AEN) and iSCSI asynchronous messages ๐Ÿ“ฃ

when it's carrying an AEN, the event details come through as sense data in the data segment ๐Ÿ“

3.5.3. Requests/Responses Carrying iSCSI Only Payload ๐Ÿ’œ

3.5.3.1. Text Request and Text Response

text requests and responses are built for parameter negotiation and as a vehicle for future extensions ๐Ÿงฉ

in the data segment, Text Requests/Responses carry text info using a simple "key=value" syntax ๐Ÿ”‘

Text Request/Responses can form extended sequences using the same Initiator Task Tag. the initiator uses the F (Final) bit in the text request header to say it's ready to end a sequence. the target uses the F (Final) bit in the text response header to say it consents to ending it ๐Ÿค

Text Requests and Responses also use the Target Transfer Tag to signal whether an operation is continuing or starting fresh ๐Ÿ”. a target that wants to continue an operation sets the Target Transfer Tag in a Text Response to a value other than the default 0xffffffff. an initiator willing to continue copies that value into the Target Transfer Tag of its next Text Request. if the initiator wants to restart the current target negotiation (start fresh), it sets the Target Transfer Tag to 0xffffffff ๐ŸŒŸ

a complete exchange is always started by the initiator, but specific parameter negotiations may be started by either the initiator or the target ๐Ÿ’…

3.5.3.2. Login Request and Login Response

Login Requests and Responses are used only during the Login Phase of each connection, to set up the session and connection parameters ๐Ÿšช. (the Login Phase is a sequence of login requests and responses carrying the same Initiator Task Tag.)

a connection is identified by an arbitrarily selected connection-ID (CID) that's unique within a session ๐Ÿ†”

like the Text Requests and Responses, Login Requests/Responses carry key=value text info with a simple syntax in the data segment ๐Ÿ”‘

the Login Phase goes through several stages (security negotiation, operational parameter negotiation). they're picked with two binary coded fields in the header: the "current stage" (CSG) and the "next stage" (NSG). the appearance of the latter gets signaled by the "transit" flag (T) ๐Ÿ›ค๏ธ

the first Login Phase of a session is the leading login, and it plays a special role ๐Ÿ‘‘. it determines some header fields (like the version number, the maximum number of connections, and the session identification) ๐Ÿ“‹

the leading login also sets the initial value of the CmdSN ๐Ÿ”ข

StatSN for each connection is initiated by the connection login ๐ŸŒฑ

a login request can imply a logout (cleanup) of the connection being logged in, which is a connection restart. to do that, it uses the same Connection ID (CID) as an existing connection, plus the same session identifying elements of the session the old connection belonged to โ™ป๏ธ

3.5.3.3. Logout Request and Response

Logout Requests and Responses are used for the orderly closing of connections for recovery or maintenance ๐Ÿงน. the logout request can be issued after a target prompts it (through an asynchronous message) or on the initiator's own initiative ๐Ÿ™‹โ€โ™€๏ธ. when it's issued on the connection being logged out, no other request may follow it ๐Ÿšซ

the Logout Response says the connection or session cleanup is complete and no other responses will arrive on that connection (if it's received on the logging-out connection) ๐ŸŽ‰. it also tells you two things in text keys: Time2Retain, how long the target will keep holding resources for recovery (like command execution that carries on over a new connection) ๐Ÿ•ฐ๏ธ, and Time2Wait, how long the initiator must wait before going ahead with recovery โณ

3.5.3.4. SNACK Request ๐Ÿ“จ

Okay so the SNACK Request is how the initiator asks the target to resend stuff it missed ๐Ÿฅบ. It's basically "hey bestie, you dropped some messages, can you send them again?" ๐Ÿ’Œ One SNACK request covers a contiguous set of missing items, called a run ๐Ÿงต, all of one type. The type is indicated in a type field in the PDU header ๐Ÿท๏ธ. The run is made up of an initial item (StatSN, DataSN, or R2TSN) plus the number of missed Status, Data, or R2T PDUs. Ngl that's giving super organized ๐Ÿ“‹.

For long Data-In sequences, the target may request (at predefined minimum intervals) a positive acknowledgement for the data it sent ๐Ÿ™‹โ€โ™€๏ธ. A SNACK request with a type field that indicates ACK and the number of Data-In PDUs acknowledged is how that positive acknowledgement gets conveyed. "Got it, thank you" energy ๐Ÿ’–.

3.5.3.5. Reject ๐Ÿšซ

Reject is how the target reports an iSCSI error condition (e.g., protocol, unsupported option) ๐Ÿ˜ฌ. It uses a Reason field in the PDU header, and it includes the complete header of the bad PDU in the Reject PDU data segment. So basically it's the receipts ๐Ÿงพ of the drama, and they're all there.

3.5.3.6. NOP-Out Request and NOP-In Response ๐Ÿ“

This request/response pair may be used by an initiator and target as a "ping" ๐Ÿ“ to verify that a connection/session is still active and all of its components are operational ๐Ÿ’“. Such a ping can be triggered by the initiator or the target ๐Ÿ”„. The triggering party says "I want a reply" by setting a value different from the default 0xffffffff in the corresponding Initiator/Target Transfer Tag ๐ŸŽฏ.

NOP-In/NOP-Out may also be used "unidirectional" ๐Ÿ›ธ to convey command, status or data counter values to the initiator/target when there's no other "carrier" ๐Ÿ“ฆ and there's a need to update the initiator/target. Main character energy for sure โœจ.


4. SCSI Mode Parameters for iSCSI โš™๏ธ

Plot twist: there are no iSCSI specific mode pages ๐Ÿ™ƒ.


5. Login and Full Feature Phase Negotiation ๐Ÿค

Okay, so iSCSI parameters get negotiated at session or connection establishment by using Login Requests and Responses (see Section 3.2.3 iSCSI Login) ๐Ÿ”, and during the Full Feature Phase (Section 3.2.4 iSCSI Full Feature Phase) by using Text Requests and Responses ๐Ÿ’ฌ. In both cases the mechanism is an exchange of iSCSI-text-key=value pairs. For brevity, iSCSI-text-keys are called just keys in the rest of this document ๐Ÿซถ.

Keys are either declarative or require negotiation, and the key description tells you which one it is ๐Ÿ“ฃ.

For the declarative keys, the declaring party sets a value for the key ๐Ÿ“. The key specification indicates if the key can be declared by the initiator, target or both ๐Ÿ‘‘.

For the keys that require negotiation, one of the parties (the proposing party) proposes a value or set of values by including the key=value in the data part of a Login or Text Request or Response PDU ๐ŸŽ€. The other party (the accepting party) makes a selection based on the value or list of values proposed, and includes the selected value in a key=value in the data part of one of the following Login or Text Response or Request PDUs ๐Ÿ’…. For most of the keys, both the initiator and target can be proposing parties ๐ŸŒˆ.

The login process proceeds in two stages: the security negotiation stage and the operational parameter negotiation stage ๐Ÿ”ฅ. Both stages are optional, but at least one of them has to be present to enable the setting of some mandatory parameters ๐Ÿงš.

If present, the security negotiation stage precedes the operational parameter negotiation stage ๐ŸŒธ.

Progression from stage to stage is controlled by the T (Transition) bit in the Login Request/Response PDU header. Through the T bit set to 1, the initiator indicates that it would like to transition. The target agrees to the transition (and selects the next stage) when it's ready ๐Ÿฆ‹. A field in the Login PDU header indicates the current stage (CSG), and during transition another field indicates the next stage (NSG) proposed (initiator) and selected (target) โœจ.

The text negotiation process is used to negotiate or declare operational parameters ๐Ÿ“. The negotiation process is controlled by the F (final) bit in the PDU header. During text negotiations, the F bit is used by the initiator to indicate that it's ready to finish the negotiation, and by the Target to acquiesce the end of negotiation ๐Ÿ’œ.

Since some key=value pairs may not fit entirely in a single PDU, the C (continuation) bit is used (both in Login and Text) to indicate that "more follows" ๐Ÿงต.

The text negotiation uses an additional mechanism by which a target may deliver larger amounts of data to an enquiring initiator ๐Ÿ“š. The target sets a Target Task Tag to be used as a bookmark: when it's returned by the initiator, it means "go on" ๐Ÿ”–. If reset to a "neutral value", it means "forget about the rest" ๐Ÿ™ˆ.

This chapter details the types of keys and values used, the syntax rules for parameter formation, and the negotiation schemes to be used with different types of parameters ๐ŸŽ€๐Ÿ’–.

5.1. Text Format

okay so the initiator and target both send little bundles of key=value pairs, and those are encoded in UTF-8 Unicode ๐Ÿ’–โœจ Every text key and text value in this doc is presented and interpreted exactly as it appears here, because they're case sensitive ๐ŸŽ€ so no capitalizing it because vibes ๐Ÿ™ˆ

Here's the list of character symbols that are allowed in text items (the hex values are the Unicode code points) ๐ŸŒธ

(a-z, A-Z) - letters
(0-9) - digits
" "  (0x20) - space
"."  (0x2e) - dot
"-"  (0x2d) - minus
"+"  (0x2b) - plus
"@"  (0x40) - commercial at
"_"  (0x5f) - underscore
"="  (0x3d) - equal
":"  (0x3a) - colon
"/"  (0x2f) - solidus or slash
"["  (0x5b) - left bracket
"]"  (0x5d) - right bracket
null (0x00) - null separator
","  (0x2c) - comma
"~"  (0x7e) - tilde

okay now the fun part ๐Ÿฆ‹ key=value pairs can span PDU boundaries, so one PDU can end mid-pair. If an initiator or target sends partial key=value text in a PDU, it says "more text is coming" by setting the C bit in the Text or Login Request or Text or Login Response to 1. A series of PDUs with C set to 1 that ends with a PDU where C is 0, or a single PDU with C set to 0, has to be considered as forming a single logical-text-data-segment (LTDS) โœจ it's giving puzzle pieces that all snap together ๐Ÿงฉ

Every key=value pair, including the last or only pair in a LTDS, MUST be followed by one null (0x00) delimiter. no exceptions bestie ๐Ÿ’…

Some quick vocab so we're all on the same page ๐Ÿ“ A key-name is whatever comes before the first "=" in the key=value pair, and the doc just calls it key for short ๐ŸŒˆ A value is whatever comes after the first "=" up to the end of the pair, but not including the null delimiter ๐Ÿ“

Next up are the definitions the rest of the doc uses ๐Ÿ‘‘ these are kind of grammar-ish so I'm keeping them verbatim in a code block:

  standard-label: A string of one or more characters that consist of
    letters, digits, dot, minus, plus, commercial at, or underscore.
    A standard-label MUST begin with a capital letter and must not
    exceed 63 characters.

  key-name: A standard-label.

  text-value: A string of zero or more characters that consist of
    letters, digits, dot, minus, plus, commercial at, underscore,
    slash, left bracket, right bracket, or colon.

  iSCSI-name-value: A string of one or more characters that consist
    of minus, dot, colon, or any character allowed by the output of
    the iSCSI string-prep template as specified in [RFC3722] (see
    also Section 3.2.6.2 iSCSI Name Encoding).

  iSCSI-local-name-value: A UTF-8 string; no null characters are
    allowed in the string.  This encoding is to be used for localized
    (internationalized) aliases.

  boolean-value: The string "Yes" or "No".

  hex-constant: A hexadecimal constant encoded as a string that
    starts with "0x" or "0X" followed by one or more digits or the
    letters a, b, c, d, e, f, A, B, C, D, E, or F.  Hex-constants are
    used to encode numerical values or binary strings.  When used to
    encode numerical values, the excessive use of leading 0 digits is
    discouraged.  The string following 0X (or 0x) represents a base16
    number that starts with the most significant base16 digit,
    followed by all other digits in decreasing order of significance
    and ending with the least-significant base16 digit.  When used to
    encode binary strings, hexadecimal constants have an implicit
    byte-length that includes four bits for every hexadecimal digit
    of the constant, including leading zeroes.  For example, a
    hex-constant of n hexadecimal digits has a byte-length of (the
    integer part of) (n+1)/2.

  decimal-constant: An unsigned decimal number with the digit 0 or a
    string of one or more digits that start with a non-zero digit.
    Decimal-constants are used to encode numerical values or binary
    strings.  Decimal constants can only be used to encode binary
    strings if the string length is explicitly specified.  There is
    no implicit length for decimal strings.  Decimal-constant MUST
    NOT be used for parameter values if the values can be equal or
    greater than 2**64 (numerical) or for binary strings that can be
    longer than 64 bits.

  base64-constant: base64 constant encoded as a string that starts
    with "0b" or "0B" followed by 1 or more digits or letters or plus
    or slash or equal.  The encoding is done according to [RFC2045]
    and each character, except equal, represents a base64 digit or a
    6-bit binary string.  Base64-constants are used to encode
    numerical-values or binary strings.  When used to encode
    numerical values, the excessive use of leading 0 digits (encoded
    as A) is discouraged.  The string following 0B (or 0b) represents
    a base64 number that starts with the most significant base64
    digit, followed by all other digits in decreasing order of
    significance and ending with the least-significant base64 digit;
    the least significant base64 digit may be optionally followed by
    pad digits (encoded as equal) that are not considered as part of
    the number.  When used to encode binary strings, base64-constants
    have an implicit
    byte-length that includes six bits for every character of the
    constant, excluding trailing equals (i.e., a base64-constant of n
    base64 characters excluding the trailing equals has a byte-length
    of ((the integer part of) (n*3/4)).  Correctly encoded base64
    strings cannot have n values of 1, 5 ... k*4+1.

  numerical-value: An unsigned integer always less than 2**64 encoded
    as a decimal-constant or a hex-constant.  Unsigned integer
    arithmetic applies to numerical-values.

  large-numerical-value: An unsigned integer that can be larger than
    or equal to 2**64 encoded as a hex constant, or
    base64-constant.  Unsigned integer arithmetic applies to
    large-numeric-values.

  numeric-range: Two numerical-values separated by a tilde where the
    value to the right of tilde must not be lower than the value to
    the left.

  regular-binary-value: A binary string not longer than 64 bits
    encoded as a decimal constant, hex constant, or base64-constant.
    The length of the string is either specified by the key
    definition or is the implicit byte-length of the encoded string.

  large-binary-value: A binary string longer than 64 bits encoded as
    a hex-constant or base64-constant.  The length of the string is
    either specified by the key definition or is the implicit
    byte-length of the encoded string.

  binary-value: A regular-binary-value or a large-binary-value.
    Operations on binary values are key specific.

  simple-value: Text-value, iSCSI-name-value, boolean-value,
    numeric-value, a numeric-range, or a binary-value.

  list-of-values: A sequence of text-values separated by a comma.

๐Ÿง default size rule: unless something else says otherwise, the maximum length of a simple-value (not its encoded form) is 255 bytes, not counting the delimiter (comma or zero byte) ๐Ÿ’€

last but definitely not least ๐Ÿ˜ญ any iSCSI target or initiator MUST support receiving at least 8192 bytes of key=value data in a negotiation sequence. And if they're proposing or accepting authentication methods that explicitly need support for very long authentication items, the initiator and target MUST support receiving at least 64 kilobytes of key=value data. that's the Simple Public-Key Mechanism (SPKM) in Appendix 11.1.2, which needs support for public key certificates ๐Ÿ”ฅ it's a whole main character moment for those big certs ๐Ÿ‘‘

5.2. Text Mode Negotiation โœจ

okay so during login (and honestly, whenever after that) some session or connection parameters get either declared or negotiated through a little text exchange ๐Ÿ’ฌ๐ŸŽ€

the initiator starts the whole thing with a Text or Login Request, and it tells the target it's ready to wrap up by setting the F bit to 1 (and keeping it at 1 in a Text Request) or the T bit in the Login Request. negotiation text can span multiple PDUs, so a Text or Login Request or Text or Login Response PDU with the C bit set to 1 MUST NOT have the F/T bit set to 1 ๐Ÿ™…โ€โ™€๏ธ

and then the bounce-back rules are so clean ๐Ÿซถ a target that gets a Text or Login Request with the C bit set to 1 MUST answer with a Text or Login Response that has no data segment (DataSegmentLength 0). and an initiator that gets a Text or Login Response with the C bit set to 1 MUST answer with a Text or Login Request that has no data segment (DataSegmentLength 0).

a target or initiator SHOULD NOT send a Text or Login Response or a Text or Login Request with no data segment (DataSegmentLength 0) unless a general or key-specific negotiation rule explicitly says it has to. no empty messages just for vibes ๐Ÿ’…

here's what a declaration looks like ๐ŸŒธ

  Declarer-> <key>=<valuex>

and the general shape of a negotiation is:

  Proposer-> <key>=<valuex>
  Acceptor-> <key>={<valuey>|NotUnderstood|Irrelevant|Reject}

so the main difference is that a declaration is a one-way textual exchange, while a negotiation is a two-way exchange ๐Ÿฆ‹

the proposer or declarer can be the initiator or the target, and the acceptor will be the target or the initiator, respectively. and targets aren't stuck just replying to whatever key=value pairs the initiator throws at them ๐Ÿงš a target is allowed to propose key=value pairs of its own, main character energy โœจ

all negotiations are explicit, which means the result MUST only be based on newly exchanged or declared values. there are no implicit proposals, so if nobody proposed something, nobody can expect a reply ๐Ÿ™ˆ and conservative design also says you shouldn't lean on default values when using some other value would have serious consequences. safety first, bestie ๐Ÿ›ก๏ธ

the value that gets proposed or declared can be a numerical-value, a numerical-range (a lower and an upper integer, separated by a tilde), a binary value, a text-value, an iSCSI-name-value, an iSCSI-local-name-value, a boolean-value (Yes or No), or a list of comma separated text-values ๐ŸŽ€ a range, a large-numerical-value, an iSCSI-name-value, and an iSCSI-local-name-value MAY ONLY be used if they're explicitly allowed. and an accepted value can be a numerical-value, a large-numerical-value, a text-value, or a boolean-value ๐Ÿ’–

if a key just isn't relevant to the current negotiation, the acceptor can answer with the constant "Irrelevant" for any type of negotiation. the negotiation still doesn't count as failed when the answer is "Irrelevant" ๐Ÿ™Œ this is for when a proposer sends a bunch of keys, but picking one value for one key makes the other keys pointless. here's how that looks ๐Ÿ‘‘

I->T OFMarker=Yes,OFMarkInt=2048~8192
T->I OFMarker=No,OFMarkInt=Irrelevant

I->T X#vkey1=(bla,alb,None),X#vkey2=(bla,alb)
T->I X#vkey1=None,X#vkey2=Irrelevant

any key the acceptor doesn't understand can be ignored without messing up the basic function ๐ŸŒˆ but the answer for a key it doesn't understand MUST be key=NotUnderstood. no silent ghosting allowed ๐Ÿ‘ป

the constants "None", "Reject", "Irrelevant", and "NotUnderstood" are reserved, and they MUST ONLY be used the way this document describes. breaking that rule is a protocol error, and that specifically includes using "Reject", "Irrelevant", and "NotUnderstood" as proposed values ๐Ÿšซ๐Ÿ˜ญ

"Reject" or "Irrelevant" are legit negotiation options where they're allowed, but using them a ton is discouraged, so don't be that person ๐Ÿ’… a negotiation counts as complete once the acceptor has sent the key value pair, even if the value is "Reject", "Irrelevant", or "NotUnderstood". sending the same key again would be a re-negotiation, and that's forbidden for lots of keys ๐Ÿ™…โ€โ™€๏ธ

if the acceptor answers with "Reject", the negotiated key just stays at its current value (or its default, if no value was ever set) ๐Ÿ“ and if the current value isn't acceptable to the proposer on the connection or to the session it was sent on, the proposer MAY choose to terminate the connection or session. dramatic, but valid โœจ

every key in this document, except for the X extension formats, MUST be supported by iSCSI initiators and targets when it's used the way it's specified here. and if it's used as specified, these keys MUST NOT be answered with NotUnderstood. no excuses, slay ๐Ÿ‘‘

now, implementers can make up new keys by prefixing them with "X-", followed by their reversed domain name, or with new keys registered with IANA by prefixing them with X#. for example, the entity that owns the domain example.com can issue something like this ๐Ÿฆ‹

X-com.example.bar.foo.do_something=3

or a registered key can be used like this ๐Ÿ’œ

X#SuperCalyPhraGilistic=Yes

implementers MAY also introduce new values, but ONLY for new keys or authentication methods (see Section 11 iSCSI Security Text Keys and Authentication Methods), or digests (see Section 12.1 HeaderDigest and DataDigest). so new values aren't a free-for-all, they're only for those specific things ๐Ÿ”

whenever a parameter's action or acceptance depends on other parameters, the dependency rules and the parameter sequence must be specified along with those parameters ๐Ÿงฉ

in the Login Phase (see Section 5.3 Login Phase), every stage is its own separate negotiation. in the FullFeaturePhase, a Text Request and Response sequence is a negotiation. negotiations MUST be handled as atomic operations ๐Ÿ’– that means all the negotiated values go into effect once the negotiation concludes in agreement, or they're all ignored if the negotiation fails. it's all or nothing, bestie ๐Ÿซถ

some parameters come with integrity rules, like "parameter-x must not exceed parameter-y" or "parameter-u not 1 implies parameter-v be Yes" ๐Ÿง  whenever it's needed, the integrity rules get specified along with the keys. and checking compliance only happens once all the parameters are available, both the existing ones and the newly negotiated ones. an iSCSI target MUST perform integrity checking before the new parameters take effect, and an initiator MAY perform integrity checking too ๐Ÿ”

finally, an iSCSI initiator or target MAY end a negotiation that doesn't wrap up within a reasonable time or number of exchanges. no one's waiting around forever ๐Ÿ˜ญโฐ


5.2.1. List negotiations

in list negotiation, the originator sends a list of values (which can include "None") ordered by its preference ๐Ÿ“‹

the responding party MUST answer with the same key and the first value it supports (and is allowed to use with that specific originator), picked from the originator's list ๐ŸŽฏ the constant "None" MUST always be used to indicate a missing function. however, "None" is only a valid selection if it was explicitly proposed ๐Ÿ™ˆ

if the acceptor doesn't understand a particular value in the list, it MUST ignore that value ๐Ÿ™ƒ and if the acceptor doesn't support, doesn't understand, or isn't allowed to use any of the proposed options with that specific originator, it may answer with the constant "Reject" or terminate the negotiation ๐Ÿ’€ selecting a value that was never proposed MUST be handled as a protocol error. no sneaky picks, periodt ๐Ÿ’…


5.2.2. Simple-value Negotiations

for simple-value negotiations, the accepting party MUST answer with the same key, and the value it picks becomes the negotiation result ๐ŸŒธ

if someone proposes a value that isn't admissible (like one that's outside the specified bounds), that MAY be answered with the constant "Reject", or the acceptor MAY pick an admissible value instead ๐Ÿฆ‹ but if the acceptor selects a value that isn't admissible under the selection rules, that's a protocol error. the selection rules are key-specific, so check the key ๐Ÿ”

for a numerical range, the value selected must be an integer within the proposed range, or it's "Reject" (if the range is unacceptable) ๐ŸŽ€

in Boolean negotiations (the ones that end up with keys taking the values Yes or No), the accepting party MUST answer with the same key and the result of the negotiation whenever the received value doesn't determine that result by itself. the last value transmitted becomes the negotiation result ๐Ÿ’œ the rules for choosing what to answer with are written as Boolean functions of the value received, plus the value the accepting party would have picked if it had a choice. it's giving logic, honestly ๐Ÿง 

specifically, the two cases where answers are OPTIONAL are:

  • The Boolean function is "AND" and the value "No" is received. The outcome of the negotiation is "No".
  • The Boolean function is "OR" and the value "Yes" is received. The outcome of the negotiation is "Yes".

everything else is REQUIRED. responses are REQUIRED in all other cases, and whatever value the acceptor chooses and sends becomes the outcome of the negotiation ๐ŸŒˆโœจ

5.3. Login Phase โœจ

okay so the Login Phase is the big glow-up moment ๐Ÿ’… where an initiator and a target connect, either spin up a brand new session or tack a connection onto one that already exists ๐Ÿฆ‹. It's where they lock in the iSCSI protocol parameters and the security parameters, and where they authenticate each other, bestie ๐Ÿ‘‘

The Login Phase only happens through Login Requests and Responses ๐ŸŽ€. The whole thing counts as one single task with one single Initiator Task Tag, kinda like those linked SCSI commands ๐Ÿ”—

The default MaxRecvDataSegmentLength is used during Login ๐ŸŒธ

The Login Phase goes in this order:

  • Login initial request
  • Login partial response (optional)
  • More Login Requests and Responses (optional)
  • Login Final-Response (mandatory)

Now the rules, and these are important, so read carefully ๐Ÿ™ˆ The initial Login Request of any connection MUST include the InitiatorName key=value pair. The initial Login Request of the first connection of a session MAY also include the SessionType key=value pair. For any connection in a session whose type is not "Discovery", the first Login Request MUST also include the TargetName key=value pair.

The Login Final-Response is the one that accepts or rejects the Login Request. That's the verdict, periodt โš–๏ธ

The Login Phase MAY include a SecurityNegotiation stage, a LoginOperationalNegotiation stage, or both, but it MUST include at least one of them. Whatever stage is included MAY be empty, apart from the mandatory names ๐Ÿงš

The Login Requests and Responses have a field (CSG) that says which negotiation stage you're in, either SecurityNegotiation or LoginOperationalNegotiation. If both stages get used, the SecurityNegotiation MUST come before the LoginOperationalNegotiation. No skipping the line here ๐Ÿ’€

Some operational parameters can also be negotiated outside of login, through Text Requests and Responses ๐Ÿ’œ

Security MUST be completely negotiated within the Login Phase. The use of underlying IPsec security is specified in Chapter 8 and in [RFC3723]. iSCSI's own in-protocol security is only authentication in the Login Phase, so that's the whole vibe ๐Ÿ”

In some environments, a target or an initiator just isn't interested in authenticating its counterpart. That's fine, because it's possible to bypass authentication through the Login Request and Response ๐ŸŒˆ

The initiator and target MAY want to negotiate iSCSI authentication parameters. Once that negotiation is done, the channel is considered secure โœจ

Most of the negotiation keys are only allowed in one specific stage. The SecurityNegotiation keys live in Chapter 11, and the LoginOperationalNegotiation keys live in Chapter 12. Only a limited set of keys (marked as Any-Stage in Chapter 12) may be used in either stage ๐Ÿฆ‹

Every Login Request or Response belongs to a specific stage, and that stage decides which negotiation keys are allowed with it. Sending a key that isn't allowed in the current stage is a protocol error, so don't do it, not me ๐Ÿ™…โ€โ™€๏ธ

Stage transitions happen through a command exchange (request/response) that carries the T bit and the same CSG code. During that exchange, the target picks the next stage using the "next stage" code (NSG). The selected NSG MUST NOT exceed the value the initiator stated. The initiator can ask for a transition whenever it's ready, but the target can only respond with a transition after the initiator has proposed one ๐ŸŽ€

Each pair of Login Request-Responses has its own T bit settings, and they have no bearing on the T bit settings of the next pair. So an initiator that set T to 1 in one pair and got a T bit of 0 back is totally allowed to send the next request with T set to 0 ๐Ÿ’…

When the initiator requests a transition and the target acknowledges it, both sides switch to the selected stage. Clean and iconic ๐Ÿ‘‘

Targets MUST NOT submit parameters that require an additional initiator Login Request in a Login Response with the T bit set to 1. Don't make the initiator do extra homework ๐Ÿ“š

Stage transitions during login (including entering and exiting) are only possible as outlined in this table, so check it before you move ๐Ÿ“

+-----------------------------------------------------------+
|From     To ->   | Security    | Operational | FullFeature |
| |               |             |             |             |
| V               |             |             |             |
+-----------------------------------------------------------+
| (start)         |  yes        |  yes        |  no         |
+-----------------------------------------------------------+
| Security        |  no         |  yes        |  yes        |
+-----------------------------------------------------------+
| Operational     |  no         |  no         |  yes        |
+-----------------------------------------------------------+

The Login Final-Response that accepts a Login Request can only come as a response to a Login Request with the T bit set to 1. Both the request and the response MUST indicate FullFeaturePhase as the next phase via the NSG field. That's the grand finale โœจ

Neither the initiator nor the target should try to declare or negotiate a parameter more than once during login. The exception is responses to specific keys that explicitly allow repeated declarations (for example, TargetAddress). Any attempt to renegotiate or redeclare parameters that aren't specifically allowed MUST be detected by the initiator and target ๐Ÿšจ. If the target catches it, the target MUST respond with Login reject (initiator error). If the initiator catches it, the initiator MUST drop the connection. Yikes, no drama allowed ๐Ÿ˜ญ

5.3.1. Login Phase Start ๐Ÿฆ‹

The Login Phase starts with a Login Request from the initiator to the target. The initial Login Request includes:

  • Protocol version supported by the initiator.
  • iSCSI Initiator Name and iSCSI Target Name
  • ISID, TSIH, and connection Ids
  • Negotiation stage that the initiator is ready to enter.

A login can create a new session, or it can add a connection to an existing session. Between a given iSCSI Initiator Node (selected only by an InitiatorName) and a given iSCSI target defined by an iSCSI TargetName and a Target Portal Group Tag, the login results are defined by this table ๐Ÿงš

+------------------------------------------------------------------+
|ISID      | TSIH        | CID    |     Target action              |
+------------------------------------------------------------------+
|new       | non-zero    | any    |     fail the login             |
|          |             |        |     ("session does not exist") |
+------------------------------------------------------------------+
|new       | zero        | any    |     instantiate a new session  |
+------------------------------------------------------------------+
|existing  | zero        | any    |     do session reinstatement   |
|          |             |        |     (see section 5.3.5)        |
+------------------------------------------------------------------+
|existing  | non-zero    | new    |     add a new connection to    |
|          | existing    |        |     the session                |
+------------------------------------------------------------------+
|existing  | non-zero    |existing|     do connection reinstatement|
|          | existing    |        |    (see section 5.3.4)         |
+------------------------------------------------------------------+
|existing  | non-zero    | any    |         fail the login         |
|          | new         |        |     ("session does not exist") |
+------------------------------------------------------------------+

Whether something counts as "existing" or "new" is decided by the target ๐Ÿ•ต๏ธโ€โ™€๏ธ

The Login Request can optionally include any of the following:

  • Security parameters OR
  • iSCSI operational parameters AND/OR
  • The next negotiation stage that the initiator is ready to enter.

The target can answer the login in these ways:

  • Login Response with Login reject. This is an immediate rejection from the target. It makes the connection terminate, and it makes the session terminate too if this is the first (or only) connection of a new session. The T bit and the CSG and NSG fields are reserved. Ouch ๐Ÿ’”
  • Login Response with Login Accept as a final response (T bit set to 1, and the NSG in both request and response set to FullFeaturePhase). The response includes the protocol version supported by the target and the session ID, and it may include iSCSI operational or security parameters (depending on the current stage) ๐ŸŽ‰
  • Login Response with Login Accept as a partial response (NSG not set to FullFeaturePhase in both request and response). This marks the start of a negotiation sequence. The response includes the protocol version supported by the target and either security or iSCSI parameters (when no security mechanism is chosen) supported by the target ๐ŸŒธ

If the initiator decides to forego the SecurityNegotiation stage, it issues the Login with the CSG set to LoginOperationalNegotiation. The target may reply with a Login Response saying it's unwilling to accept the connection without SecurityNegotiation (see Section 10.13 Login Response), and it will terminate the connection with a response of Authentication failure (see Section 10.13.5 Status-Class and Status-Detail). Not the vibe ๐Ÿ˜ฌ

If the initiator is willing to negotiate iSCSI security, but doesn't want to make the initial parameter proposal and is okay accepting a connection without iSCSI security, it issues the Login with the T bit set to 1, the CSG set to SecurityNegotiation, and the NSG set to LoginOperationalNegotiation. If the target is also ready to skip security, the Login Response only contains the TargetPortalGroupTag key (see Section 12.9 TargetPortalGroupTag), with the T bit set to 1, the CSG set to SecurityNegotiation, and the NSG set to LoginOperationalNegotiation ๐Ÿซถ

An initiator that wants to operate without iSCSI security, with all the operational parameters at their default values, issues the Login with the T bit set to 1, the CSG set to LoginOperationalNegotiation, and the NSG set to FullFeaturePhase. If the target is also ready to forego security and can finish its LoginOperationalNegotiation, the Login Response has the T bit set to 1, the CSG set to LoginOperationalNegotiation, and the NSG set to FullFeaturePhase in the next stage. Easy, breezy, beautiful ๐ŸŒˆ

During the Login Phase, the iSCSI target MUST return the TargetPortalGroupTag key with the first Login Response PDU that it's allowed to include it in (that is, the first Login Response issued after the first Login Request with the C bit set to 0). This applies for all session types when TargetName is given and the response is not a redirection. The TargetPortalGroupTag value tells you which iSCSI portal group is servicing the Login Request PDU. If portal group reconfiguration is a concern in a given environment, the iSCSI initiator should use this key to make sure it really started the Login Phase with the intended target portal group ๐Ÿ”โœจ

5.3.2. iSCSI Security Negotiation ๐Ÿ”

okay bestie, the security exchange is where things get serious ๐Ÿ”’โœจ It sets the security mechanism and authenticates the initiator user and the target to each other ๐Ÿซถ. It follows the authentication method picked during the negotiation phase, and it all runs through the key=value parameters inside the Login Requests and responses ๐Ÿ’….

an initiator directed negotiation goes like this ๐ŸŽ€:

  • The initiator sends a Login Request with an ordered list of the options it supports (authentication algorithm). The options are listed in the initiator's order of preference. The initiator MAY also send private or public extension options. ๐ŸŒธ
  • The target MUST reply with the first option in the list it supports and is allowed to use for the specific initiator unless it does not support any, in which case it MUST answer with "Reject" (see Section 5.2 Text Mode Negotiation). The parameters are encoded in UTF8 as key=value. For security parameters, see Chapter 11. ๐Ÿ”ฅ
  • When the initiator is ready to wrap up the SecurityNegotiation stage, it sets the T bit to 1 and the NSG to whatever stage it would like next. The target then sets the T bit to 1 and sets the NSG to the next stage in the Login Response once it finishes sending its security keys. The next stage will be the one the target selected. If the next stage is FullFeaturePhase, the target MUST respond with a Login Response with the TSIH value. โœจ

if the security negotiation fails at the target, the target MUST send the appropriate Login Response PDU ๐Ÿ’€. if it fails at the initiator, the initiator SHOULD close the connection ๐Ÿ™ˆ.

also, the negotiation might be directed by the target instead, if the initiator does support security but isn't ready to direct the negotiation (propose options) ๐Ÿงš.

5.3.3. Operational Parameter Negotiation During the Login Phase ๐Ÿ“

operational parameter negotiation during the login MAY be done ๐ŸŒˆ:

  • Starting with the first Login Request if the initiator does not propose any security/integrity option.
  • Starting immediately after the security negotiation if the initiator and target perform such a negotiation.

operational parameter negotiation MAY involve several Login Request-Response exchanges, all started and ended by the initiator ๐Ÿ’œ. The initiator MUST indicate its intent to end the negotiation by setting the T bit to 1, and the target sets the T bit to 1 on the last response ๐Ÿซถ.

if the target answers a Login Request that has the T bit set to 1 with a Login Response that has the T bit set to 0, the initiator should keep sending the Login Request (even empty) with the T bit set to 1, for as long as it still wants to switch stage ๐Ÿฆ‹. It keeps doing this until it gets a Login Response with the T bit set to 1, or it gets a key that requires it to set the T bit to 0 ๐Ÿ’….

some session specific parameters can only be set during the Login Phase of the first connection of a session (i.e., begun by a Login Request that contains a zero-valued TSIH). That's the leading Login Phase, and an example is the maximum number of connections that can be used for this session ๐Ÿ‘‘.

a session is operational once it has at least one connection in FullFeaturePhase. New or replacement connections can only be added after that point โœจ.

for operational parameters, see Chapter 12 ๐ŸŒธ.

5.3.4. Connection Reinstatement ๐Ÿฆ‹

connection reinstatement is when an initiator logs in with an ISID-TSIH-CID combination that might still be active from the target's point of view ๐Ÿ˜ญ. That causes the implicit logout of the connection that matches the CID, and a fresh Full Feature Phase iSCSI connection takes its place (with the same CID) ๐Ÿ”.

so the TSIH in the Login PDU MUST be non-zero, and the CID doesn't change during a connection reinstatement ๐Ÿ’–. The Login Request does the logout job for the old connection if an explicit logout didn't happen earlier. In sessions with a single connection, this might mean opening a second connection with the sole purpose of cleaning up the first ๐Ÿงน. Targets MUST support opening a second connection even when they don't support multiple connections in Full Feature Phase if ErrorRecoveryLevel is 2, and SHOULD support opening a second connection if ErrorRecoveryLevel is less than 2 ๐Ÿ“.

if the operational ErrorRecoveryLevel is 2, connection reinstatement enables future task reassignment ๐Ÿ’…. If the operational ErrorRecoveryLevel is less than 2, connection reinstatement just replaces the old CID and does not enable task reassignment. In that case, all the tasks that were active on the old CID must be immediately terminated without further notice to the initiator โš ๏ธ.

the initiator connection state MUST be CLEANUP_WAIT (section 7.1.3) when it attempts a connection reinstatement ๐ŸŽ€.

in practical terms, on top of the implicit logout of the old connection, reinstatement works just like a new connection login ๐ŸŒˆ.

5.3.5. Session Reinstatement, Closure, and Timeout โณ

session reinstatement is when the initiator logs in with an ISID that might still be active from the target's point of view ๐Ÿ‘€. That implicitly logs out the session that matches the ISID, and a new iSCSI session takes its place (with the same ISID) ๐Ÿ’ซ. So the TSIH in the Login PDU MUST be zero to signal session reinstatement ๐Ÿงš. Session reinstatement causes all the tasks that were active on the old session to be immediately terminated by the target, without further notice to the initiator ๐Ÿ’€.

the initiator session state MUST be FAILED (Section 7.3 Session State Diagrams) when it attempts a session reinstatement ๐Ÿ™ˆ.

session closure is an event that's one of the following:

  • A successful "session close" logout.
  • A successful "connection close" logout for the last Full Feature Phase connection, when no other connection in the session is waiting for cleanup (Section 7.2 Connection Cleanup State Diagram for Initiators and Targets) and no tasks in the session are waiting for reassignment.

session timeout is an event that happens when the last connection state timeout expires and no tasks are waiting for reassignment. It takes the session to the FREE state (N6 transition in the session state diagram) ๐Ÿ˜ค.

5.3.5.1. Loss of Nexus Notification ๐Ÿ“ฃ

the iSCSI layer tells the SCSI layer about an "I_T nexus loss" when any one of these events happens:

  • a) Successful completion of session reinstatement.
  • b) Session closure event.
  • c) Session timeout event.

certain SCSI object clearing actions may happen because of that notification in the SCSI end nodes, as documented in Appendix F. - Clearing Effects of Various Events on Targets - ๐Ÿงนโœจ.

5.3.6. Session Continuation and Failure ๐Ÿ”„

session continuation is when the state of an existing session keeps getting used, either through connection reinstatement (Section 5.3.4 Connection Reinstatement) or by adding a connection with a new CID. Either way, the new transport connection gets tied to the session state ๐Ÿซถ.

session failure is an event where the last Full Feature Phase connection reaches the CLEANUP_WAIT state (Section 7.2 Connection Cleanup State Diagram for Initiators and Targets), or finishes a successful recovery logout. That causes all the active tasks that used to belong to the connection to start waiting for task reassignment ๐Ÿ˜ญ๐Ÿ’”.

5.4. Operational Parameter Negotiation Outside the Login Phase ๐ŸŒท

some operational parameters MAY be negotiated outside (after) the Login Phase ๐Ÿ’œ.

parameter negotiation in Full Feature Phase happens through Text requests and responses. Operational parameter negotiation MAY involve several Text request-response exchanges, which the initiator always starts and ends using the same Initiator Task Tag ๐ŸŽ€. The initiator MUST indicate its intent to end the negotiation by setting the F bit to 1, and the target sets the F bit to 1 on the last response ๐Ÿ’….

if the target answers a Text request with the F bit set to 1 and a Text response with the F bit set to 0, the initiator should keep sending the Text request (even empty) with the F bit set to 1, for as long as it still wants to finish the negotiation. It keeps going until it gets a Text response with the F bit set to 1 ๐Ÿฆ‹. Answering a Text request with the F bit set to 1 with an empty response (no key=value pairs) that has the F bit set to 0 is discouraged ๐Ÿ™ˆ.

Targets MUST NOT put parameters that need another initiator Text request into a Text response with the F bit set to 1 ๐Ÿšซ.

in a negotiation sequence, the F bit settings in one pair of Text request-responses have no effect on the F bit settings in the next pair. An initiator that has the F bit set to 1 in a request and gets an F bit setting of 0 back may send its next request with the F bit set to 0 โœจ.

whenever the target responds with the F bit set to 0, it MUST set the Target Transfer Tag to a value other than the default 0xffffffff ๐Ÿ”ฅ.

an initiator MAY reset an operational parameter negotiation by sending a Text request with the Target Transfer Tag set to 0xffffffff, after it gets a response with a Target Transfer Tag set to a value other than 0xffffffff ๐Ÿ”. A target may reset an operational parameter negotiation by answering a Text request with a Reject PDU ๐ŸŒˆ.

neither the initiator nor the target should try to declare or negotiate a parameter more than once during any negotiation sequence, unless there's an operational parameter negotiation reset in between. The exception is responses to specific keys that explicitly allow repeated key declarations (e.g., TargetAddress) ๐Ÿ‘€. If the target detects this, it MUST result in a Reject PDU with a reason of "protocol error" ๐Ÿ’€. The initiator MUST reset the negotiation as outlined above ๐Ÿงน.

parameters negotiated by a text exchange only become effective after the negotiation sequence is completed โœจ.

6. iSCSI Error Handling and Recovery ๐Ÿ›Ÿ

6.1. Overview ๐ŸŒธ

6.1.1. Background ๐Ÿ’ญ

the following two things are why a lot of the error recovery stuff in iSCSI exists ๐Ÿ‘‘:

  • i) An iSCSI PDU may fail the digest check and be dropped, even though the TCP layer received it. The iSCSI layer must optionally be allowed to recover such dropped PDUs.
  • ii) A TCP connection may fail at any time during the data transfer. All the active tasks must optionally be allowed to continue on a different TCP connection within the same session.

implementations have a lot of flexibility in deciding how much error recovery to support, when to use it, and which mechanisms to use to get the required behavior ๐ŸŽ€. Only the externally visible actions of the error recovery mechanisms must be standardized, so things stay interoperable ๐Ÿซถ.

this chapter describes a general model for recovery that supports interoperability. See Appendix E. - Algorithmic Presentation of Error Recovery Classes - for more detail on how the described model could be implemented ๐Ÿงš. Compliant implementations don't have to match the implementation details of this model as presented, but their external behavior must correspond to the externally observable characteristics of the presented model ๐Ÿ’…โœจ.

6.1.2. Goals

okay so bestie, here are the major design goals for the iSCSI error recovery scheme ๐ŸŽ€โœจ

  • a) Let iSCSI implementations meet different requirements by defining a collection of error recovery mechanisms they can pick from ๐Ÿ’…
  • b) Make sure any two implementations with different sets of error recovery capabilities can still interoperate ๐Ÿซถ
  • c) Define the error recovery mechanisms so command ordering survives even when errors happen, for initiators that need ordering ๐Ÿฆ‹
  • d) Don't add anything to the fast path, but moderate complexity in the error recovery path is totally fine ๐Ÿงš
  • e) Stop the initiator and the target from both trying to recover the same set of PDUs at the same time ๐Ÿ˜ญ there has to be a clear "error recovery functionality distribution" between them, no drama

6.1.3. Protocol Features and State Expectations

okay so the initiator mechanisms for error recovery are ๐ŸŒธ

  • a) NOP-OUT to probe the sequence numbers of the target (section 10.18)
  • b) Command retry (section 6.2.1)
  • c) Recovery R2T support (section 6.7)
  • d) Requesting retransmission of status/data/R2T using the SNACK facility (section 10.16)
  • e) Acknowledging receipt of the data (section 10.16)
  • f) Reassigning the connection allegiance of a task to a different TCP connection (section 6.2.2)
  • g) Terminating the entire iSCSI session to start afresh (section 6.1.4.4)

and the target mechanisms are ๐Ÿ’œ

  • a) NOP-IN to probe the sequence numbers of the initiator (section 10.19)
  • b) Requesting retransmission of data using the recovery R2T feature (section 6.7)
  • c) SNACK support (section 10.16)
  • d) Requesting that parts of read data be acknowledged (section 10.7.2)
  • e) Allegiance reassignment support (section 6.2.2)
  • f) Terminating the entire iSCSI session to force the initiator to start over (section 6.1.4.4)

so for any outstanding SCSI command, it's assumed that iSCSI, working together with SCSI at the initiator, can keep enough info to rebuild the command PDU, and that the outgoing data is still sitting in host memory so it can be retransmitted while the command is outstanding ๐Ÿง  at the target, incoming data (read data) MAY be kept for recovery, or it can be reread from a device server. it's giving organized, no cap ๐Ÿ“

it's also assumed that a target will keep the "status & sense" for a command it has executed if it supports status retransmission. and a target that agrees to support data retransmission is expected to be ready to retransmit the outgoing data (i.e., Data-In) on request until either the status for the completed command is acknowledged, or the data in question has been separately acknowledged. main character energy from the target side ๐Ÿ‘‘

6.1.4. Recovery Classes

okay so iSCSI gives you these classes of recovery, listed in order of increasing scope of affected iSCSI tasks ๐ŸŒˆ

  • Within a command (i.e., without requiring command restart).
  • Within a connection (i.e., without requiring the connection to be rebuilt, but perhaps requiring command restart).
  • Connection recovery (i.e., perhaps requiring connections to be rebuilt and commands to be reissued).
  • Session recovery.

the recovery scenarios in the rest of this section are representative, not exclusive, so they aren't a full list of everything that can happen ๐Ÿ™ˆ in every case, they describe the lowest class of recovery that MAY be attempted. the implementer gets to decide when to escalate to the next recovery class and/or which recovery classes to implement. both the iSCSI target and initiator MAY escalate the error handling to an error recovery class, which affects a larger number of iSCSI tasks in any of the cases in the following discussion โœจ

in all classes, the implementer can choose to defer errors to the SCSI initiator (with an appropriate response code). in that case the task, if there is one, has to be removed from the target, and all the side effects, such as ACA, must be considered ๐Ÿ’…

Within-connection and within-command recovery classes MUST NOT be attempted before the connection is in Full Feature Phase. ๐Ÿšซ

in the detailed descriptions of the recovery classes, the mandating terms (MUST, SHOULD, MAY, etc.) mark normative actions that have to be executed if the recovery class is supported and used ๐ŸŽ€

6.1.4.1. Recovery Within-command

okay so at the target, these cases are good candidates for within-command recovery ๐Ÿฆ‹

  • Lost data PDU, which shows up through one of these:
    • a) Data digest error, handled as specified in Section 6.7 Digest Errors, using the option of a recovery R2T.
    • b) Sequence reception timeout (no data or partial-data-and-no-F-bit), treated as an implicit sequence error and handled as specified in Section 6.8 Sequence Errors, using the option of a recovery R2T.
    • c) Header digest error, which shows up as a sequence reception timeout or a sequence error, handled as specified in Section 6.8 Sequence Errors, using the option of a recovery R2T.

and at the initiator, these cases are good candidates for within-command recovery ๐ŸŒธ

  • Lost data PDU or lost R2T, which shows up through one of these:
    • a) Data digest error, handled as specified in Section 6.7 Digest Errors, using the option of a SNACK.
    • b) Sequence reception timeout (no status) or response reception timeout, handled as specified in Section 6.8 Sequence Errors, using the option of a SNACK.
    • c) Header digest error, which shows up as a sequence reception timeout or a sequence error, handled as specified in Section 6.8 Sequence Errors, using the option of a SNACK.

to avoid a race with the target, which might already have a recovery R2T or a termination response on its way, an initiator SHOULD NOT originate a SNACK for an R2T based on its internal timeouts (if it has any) ๐Ÿ™…โ€โ™€๏ธ recovery in this case is better left to the target, trust ๐Ÿซถ

the timeout values used by the initiator and target are outside the scope of this document. sequence reception timeout is generally a large enough value to let the data sequence transfer finish, so it's not gonna cut things off too early ๐ŸŒˆ

6.1.4.2. Recovery Within-connection

okay so at the initiator, these cases are good candidates for within-connection recovery ๐Ÿงš

  • Requests not acknowledged for a long time. requests get acknowledged explicitly through ExpCmdSN, or implicitly by receiving data and/or status. the initiator MAY retry non-acknowledged commands as specified in Section 6.2 Retry and Reassign in Recovery ๐Ÿ”ฅ
  • Lost iSCSI numbered Response. you can spot it by either finding a data digest error on a Response PDU or a Data-In PDU carrying the status, or by receiving a Response PDU with a higher StatSN than expected. in the first case, digest error handling is done as specified in Section 6.7 Digest Errors, using the option of a SNACK. in the second case, sequence error handling is done as specified in Section 6.8 Sequence Errors, using the option of a SNACK ๐Ÿ’–

and at the target, these cases are good candidates for within-connection recovery ๐Ÿ“

  • Status/Response not acknowledged for a long time. the target MAY issue a NOP-IN (with a valid Target Transfer Tag or otherwise) that carries the next status sequence number it's going to use in the StatSN field. this helps the initiator detect any missing StatSN values and issue a SNACK for the status ๐Ÿซถ

the timeout values used by the initiator and the target are outside the scope of this document ๐Ÿ™ˆ

6.1.4.3. Connection Recovery

okay so at an iSCSI initiator, these cases are good candidates for connection recovery ๐Ÿ’…

  • TCP connection failure: the initiator MUST close the connection. then it MUST either implicitly or explicitly log out the failed connection with the reason code remove the connection for recovery, and reassign connection allegiance for all commands still in progress that were associated with the failed connection, on one or more connections (some or all of which MAY be newly established connections), using the Task reassign task management function (see Section 10.5.1 Function). for an initiator, a command is in progress for as long as it hasn't received a response or a Data-In PDU including status ๐Ÿฆ‹

Note: The logout function is mandatory. However, a new connection establishment is only mandatory if the failed connection was the last or only connection in the session. ๐Ÿ“

  • Receiving an Asynchronous Message that indicates one or all connections in a session have been dropped. the initiator MUST handle it as a TCP connection failure for the connection(s) referred to in the Message ๐Ÿšจ

and at an iSCSI target, these cases are good candidates for connection recovery ๐ŸŒธ

  • TCP connection failure. the target MUST close the connection and, if more than one connection is available, the target SHOULD send an Asynchronous Message that indicates it has dropped the connection. then the target waits for the initiator to continue recovery ๐Ÿงš

6.1.4.4. Session Recovery

okay so session recovery should be performed when all the other recovery attempts have failed ๐Ÿ˜ญ very simple initiators and targets MAY perform session recovery on all iSCSI errors and rely on recovery at the SCSI layer and above, which is honestly so relatable ๐Ÿ’€

session recovery means closing all TCP connections, internally aborting all executing and queued tasks for the given initiator at the target, terminating all outstanding SCSI commands with an appropriate SCSI service response at the initiator, and restarting a session on a new set of connection(s) (TCP connection establishment and login on all new connections). it's a full reset, periodt ๐Ÿ”ฅ

for the possible clearing effects of session recovery on SCSI and iSCSI objects, check out Appendix F. - Clearing Effects of Various Events on Targets - ๐ŸŒˆ

6.1.5. Error Recovery Hierarchy ๐Ÿชœ

okay so the error recovery classes we just went through? they're organized into a hierarchy bc it makes them way easier to understand AND keeps the implementation complexity from getting out of hand ๐Ÿ’… few, well-defined levels means interoperability is easier to achieve ๐Ÿซถ the attributes of this hierarchy are as follows:

  • a) Each level is a superset of the capabilities of the previous level. For example, Level 1 support implies supporting all capabilities of Level 0 and more.
  • b) As a corollary, supporting a higher error recovery level means increased sophistication and possibly an increase in resource requirements.
  • c) Supporting error recovery level "n" is advertised and negotiated by each iSCSI entity by exchanging the text key "ErrorRecoveryLevel=n". The lower of the two exchanged values is the operational ErrorRecoveryLevel for the session.

the following diagram represents the error recovery hierarchy ๐ŸŒˆ

                     +
                    /
                   / 2 \       <-- Connection recovery
                  +-----+
                 /   1   \     <-- Digest failure recovery
                +---------+
               /     0     \   <-- Session failure recovery
              +-------------+

the following table lists the error recovery capabilities expected from the implementations that support each error recovery level ๐Ÿ“

+-------------------+--------------------------------------------+
|ErrorRecoveryLevel |  Associated Error recovery capabilities    |
+-------------------+--------------------------------------------+
|        0          |  Session recovery class                    |
|                   |  (Section 6.1.4.4 Session Recovery)        |
+-------------------+--------------------------------------------+
|        1          |  Digest failure recovery (See Note below.) |
|                   |  plus the capabilities of ER Level 0       |
+-------------------+--------------------------------------------+
|        2          |  Connection recovery class                 |
|                   |  (Section 6.1.4.3 Connection Recovery)     |
|                   |  plus the capabilities of ER Level 1       |
+-------------------+--------------------------------------------+

Note: Digest failure recovery is comprised of two recovery classes: Within-Connection recovery class (Section 6.1.4.2 Recovery Within-connection) and Within-Command recovery class (Section 6.1.4.1 Recovery Within-command).

when a defined value of ErrorRecoveryLevel is proposed by an originator in a text negotiation, the originator MUST support the functionality defined for the proposed value and additionally, the functionality corresponding to any defined value numerically less than the proposed. when a defined value of ErrorRecoveryLevel is returned by a responder in a text negotiation, the responder MUST support the functionality corresponding to the ErrorRecoveryLevel it is accepting.

when either party attempts to use error recovery functionality beyond what is negotiated, the recovery attempts MAY fail unless an a priori agreement outside the scope of this document exists between the two parties to provide such support.

Implementations MUST support error recovery level "0", while the rest are OPTIONAL to implement. in implementation terms, the above striation means that the following incremental sophistication with each level is required ๐Ÿ”ฅ

+-------------------+---------------------------------------------+
|Level transition   |  Incremental requirement                    |
+-------------------+---------------------------------------------+
|        0->1       |  PDU retransmissions on the same connection |
+-------------------+---------------------------------------------+
|        1->2       |  Retransmission across connections and      |
|                   |  allegiance reassignment                    |
+-------------------+---------------------------------------------+

6.2. Retry and Reassign in Recovery ๐Ÿ”

this section summarizes two important and somewhat related iSCSI protocol features used in error recovery ๐Ÿงš

6.2.1. Usage of Retry

by resending the same iSCSI command PDU ("retry") in the absence of a command acknowledgement (by way of an ExpCmdSN update) or a response, an initiator attempts to "plug" (what it thinks are) the discontinuities in CmdSN ordering on the target end. Discarded command PDUs, due to digest errors, may have created these discontinuities ๐Ÿ™ˆ

Retry MUST NOT be used for reasons other than plugging command sequence gaps, and in particular, cannot be used for requesting PDU retransmissions from a target. Any such PDU retransmission requests for a currently allegiant command in progress may be made using the SNACK mechanism described in section 10.16, although the usage of SNACK is OPTIONAL.

if initiators, as part of plugging command sequence gaps as described above, inadvertently issue retries for allegiant commands already in progress (i.e., targets did not see the discontinuities in CmdSN ordering), the duplicate commands are silently ignored by targets as specified in section 3.2.2.1 โœจ

When an iSCSI command is retried, the command PDU MUST carry the original Initiator Task Tag and the original operational attributes (e.g., flags, function names, LUN, CDB etc.) as well as the original CmdSN. The command being retried MUST be sent on the same connection as the original command unless the original connection was already successfully logged out.

6.2.2. Allegiance Reassignment ๐Ÿ’œ

by issuing a "task reassign" task management request (Section 10.5.1 Function), the initiator signals its intent to continue an already active command (but with no current connection allegiance) as part of connection recovery. This means that a new connection allegiance is requested for the command, which seeks to associate it to the connection on which the task management request is being issued. Before the allegiance reassignment is attempted for a task, an implicit or explicit Logout with the reason code "remove the connection for recovery" ( see section 10.14) MUST be successfully completed for the previous connection to which the task was allegiant.

in reassigning connection allegiance for a command, the targets SHOULD continue the command from its current state. For example, when reassigning read commands, the target SHOULD take advantage of the ExpDataSN field provided by the Task Management function request (which must be set to zero if there was no data transfer) and bring the read command to completion by sending the remaining data and sending (or resending) the status. ExpDataSN acknowledges all data sent up to, but not including, the Data-In PDU and or R2T with DataSN (or R2TSN) equal to ExpDataSN. However, targets may choose to send/receive all unacknowledged data or all of the data on a reassignment of connection allegiance if unable to recover or maintain an accurate state. Initiators MUST not subsequently request data retransmission through Data SNACK for PDUs numbered less than ExpDataSN (i.e., prior to the acknowledged sequence number). For all types of commands, a reassignment request implies that the task is still considered in progress by the initiator and the target must conclude the task appropriately if the target returns the "Function Complete" response to the reassignment request. This might possibly involve retransmission of data/R2T/status PDUs as necessary, but MUST involve the (re)transmission of the status PDU.

It is OPTIONAL for targets to support the allegiance reassignment. This capability is negotiated via the ErrorRecoveryLevel text key during the login time. When a target does not support allegiance reassignment, it MUST respond with a Task Management response code of "Allegiance reassignment not supported". If allegiance reassignment is supported by the target, but the task is still allegiant to a different connection, or a successful recovery Logout of the previously allegiant connection was not performed, the target MUST respond with a Task Management response code of "Task still allegiant".

If allegiance reassignment is supported by the target, the Task Management response to the reassignment request MUST be issued before the reassignment becomes effective.

If a SCSI Command that involves data input is reassigned, any SNACK Tag it holds for a final response from the original connection is deleted and the default value of 0 MUST be used instead.


6.3. Usage Of Reject PDU in Recovery ๐Ÿšซ

Targets MUST NOT implicitly terminate an active task by sending a Reject PDU for any PDU exchanged during the life of the task. If the target decides to terminate the task, a Response PDU (SCSI, Text, Task, etc.) must be returned by the target to conclude the task. If the task had never been active before the Reject (i.e., the Reject is on the command PDU), targets should not send any further responses because the command itself is being discarded.

The above rule means that the initiator can eventually expect a response on receiving Rejects, if the received Reject is for a PDU other than the command PDU itself. The non-command Rejects only have diagnostic value in logging the errors, and they can be used for retransmission decisions by the initiators.

The CmdSN of the rejected command PDU (if it is a non-immediate command) MUST NOT be considered received by the target (i.e., a command sequence gap must be assumed for the CmdSN), even though the CmdSN of the rejected command PDU may be reliably ascertained. Upon receiving the Reject, the initiator MUST plug the CmdSN gap in order to continue to use the session. The gap may be plugged either by transmitting a command PDU with the same CmdSN, or by aborting the task (see section 6.9 on how an abort may plug a CmdSN gap) ๐Ÿงฉ

When a data PDU is rejected and its DataSN can be ascertained, a target MUST advance ExpDataSN for the current data burst if a recovery R2T is being generated. The target MAY advance its ExpDataSN if it does not attempt to recover the lost data PDU.


6.4. Connection Timeout Management โณ

iSCSI defines two session-global timeout values (in seconds) - Time2Wait and Time2Retain - that are applicable when an iSCSI Full Feature Phase connection is taken out of service either intentionally or by an exception. Time2Wait is the initial "respite time" before attempting an explicit/implicit Logout for the CID in question or task reassignment for the affected tasks (if any). Time2Retain is the maximum time after the initial respite interval that the task and/or connection state(s) is/are guaranteed to be maintained on the target to cater to a possible recovery attempt. Recovery attempts for the connection and/or task(s) SHOULD NOT be made before Time2Wait seconds, but MUST be completed within Time2Retain seconds after that initial Time2Wait waiting period. ๐ŸŒธ

6.4.1. Timeouts on Transport Exception Events

A transport connection shutdown or a transport reset without any preceding iSCSI protocol interactions informing the end-points of the fact causes a Full Feature Phase iSCSI connection to be abruptly terminated. The timeout values to be used in this case are the negotiated values of defaultTime2Wait (Section 12.15 DefaultTime2Wait) and DefaultTime2Retain (Section 12.16 DefaultTime2Retain) text keys for the session. ๐Ÿ’–

6.4.2. Timeouts on Planned Decommissioning ๐Ÿงš

okay bestie, before any planned decommissioning of a Full Feature Phase iSCSI connection, there's always either a Logout Response PDU or an Async Message PDU ๐Ÿซถ. The Time2Wait and Time2Retain field values (section 10.15) in a Logout Response PDU, and the Parameter2 and Parameter3 fields of an Async Message (AsyncEvent types "drop the connection" or "drop all the connections"; section 10.9.1), are what set the timeout values for each of those cases ๐Ÿ’….

these timeout values only apply to the affected connection and the tasks active on it โœจ. they don't touch any initiator timers (if there are any) that are already running on connections or tasks in that session ๐Ÿ™ˆ it's giving very clear boundaries ๐Ÿ’œ

6.5. Implicit Termination of Tasks ๐Ÿ”ฅ

a target implicitly terminates its active tasks in these cases, because of how iSCSI protocol dynamics work ๐Ÿฆ‹

  • a) When a connection is implicitly or explicitly logged out with the reason code of "Close the connection" and there are active tasks allegiant to that connection.
  • b) When a connection fails and the connection state eventually times out (state transition M1 in Section 7.2.2 State Transition Descriptions for Initiators and Targets) and there are active tasks allegiant to that connection.
  • c) When a successful Logout with the reason code of "remove the connection for recovery" is performed while there are active tasks allegiant to that connection, and those tasks eventually time out after the Time2Wait and Time2Retain periods without allegiance reassignment.
  • d) When a connection is implicitly or explicitly logged out with the reason code of "Close the session" and there are active tasks in that session.

if the tasks terminated in cases a), b, c) and d)are SCSI tasks, they must be internally terminated as if with CHECK CONDITION status ๐Ÿง . this status only matters for handling the internal SCSI state and the SCSI side effects around ordering, because it's never sent back to the initiator as a terminating status ๐Ÿคซ. still, extra stuff may have to happen at the SCSI level depending on the SCSI context the SCSI standards define (e.g., queued commands and ACA, in cases a), b), and c), after the tasks are terminated, the target MUST report a Unit Attention condition on the next command processed on any connection for each affected I_T_L nexus with the status of CHECK CONDITION, and the ASC/ASCQ value of 47h/7Fh - "SOME COMMANDS CLEARED BY ISCSI PROTOCOL EVENT" , etc. - see [SAM2] and [SPC3]). main character energy for the SCSI layer ๐Ÿ‘‘

6.6. Format Errors ๐ŸŽ€

only two explicit violations of the PDU layout rules count as format errors ๐Ÿ’€:

  • a) Illegal contents of any PDU header field except the Opcode (legal values are specified in Section 10 iSCSI PDU Formats).
  • b) Inconsistent field contents (consistent field contents are specified in Section 10 iSCSI PDU Formats).

format errors point to a major implementation flaw in one of the parties ๐Ÿ˜ญ

when a target or an initiator gets an iSCSI PDU with a format error, it MUST immediately terminate all transport connections in the session, either with a connection close or with a connection reset, and escalate the format error to session recovery (see Section 6.1.4.4 Session Recovery) ๐Ÿšจ

6.7. Digest Errors ๐Ÿ’–

the legal choices for handling digest errors below leave out session recovery as an explicit option ๐Ÿ™ƒ but either party that detects a digest error may choose to escalate it to session recovery ๐Ÿงš

when a target or an initiator receives any iSCSI PDU with a header digest error, it MUST either discard the header and all data up to the beginning of a later PDU or close the connection. because a header digest error means the length field may have been corrupted, the start of a later PDU has to be reliably found by other means, such as the operation of a sync and steering layer ๐Ÿ‘€

when a target receives any iSCSI PDU with a payload digest error, it MUST answer with a Reject PDU with a reason code of Data-Digest-Error and discard the PDU ๐Ÿซ 

  • If the discarded PDU is a solicited or unsolicited iSCSI data PDU (for immediate data in a command PDU, non-data PDU rule below applies), the target MUST do one of the following:
    • a) Request retransmission with a recovery R2T.
    • b) Terminate the task with a response PDU with a CHECK CONDITION Status and an iSCSI Condition of "protocol service CRC error" (Section 10.4.7.2 Sense Data). If the target chooses to implement this option, it MUST wait to receive all the data (signaled by a Data PDU with the final bit set for all outstanding R2Ts) before sending the response PDU. A task management command (such as an abort task) from the initiator during this wait may also conclude the task.
  • No further action is necessary for targets if the discarded PDU is a non-data PDU. In case of immediate data being present on a discarded command, the immediate data is implicitly recovered when the task is retried (see section 6.2.1), followed by the entire data transfer for the task. ๐Ÿ”

when an initiator receives any iSCSI PDU with a payload digest error, it MUST discard the PDU ๐Ÿ—‘๏ธ

  • If the discarded PDU is an iSCSI data PDU, the initiator MUST do one of the following:
    • a) Request the desired data PDU through SNACK. In response to the SNACK, the target MUST either resend the data PDU or reject the SNACK with a Reject PDU with a reason code of "SNACK reject", in which case:
      • i) If the status has not already been sent for the command, the target MUST terminate the command with a CHECK CONDITION Status and an iSCSI Condition of "SNACK rejected" (Section 10.4.7.2 Sense Data).
      • ii) If the status was already sent, no further action is necessary for the target. The initiator in this case MUST wait for the status to be received and then discard it, so as to internally signal the completion with CHECK CONDITION Status and an iSCSI Condition of "protocol service CRC error" (Section 10.4.7.2 Sense Data).
    • b) Abort the task and terminate the command with an error.
  • If the discarded PDU is a response PDU, the initiator MUST do one of the following:
    • a) Request PDU retransmission with a status SNACK.
    • b) Logout the connection for recovery and continue the tasks on a different connection instance as described in Section 6.2 Retry and Reassign in Recovery.
    • c) Logout to close the connection (abort all the commands associated with the connection).
  • No further action is necessary for initiators if the discarded PDU is an unsolicited PDU (e.g., Async, Reject). Task timeouts (as in the initiator waiting for a command completion) or process timeouts (as in the target waiting for a Logout) will ensure that the correct operational behavior results in these cases despite the discarded PDU. โณ

6.8. Sequence Errors ๐Ÿงฉ

when an initiator receives an iSCSI R2T/data PDU with an out of order R2TSN/DataSN, or a SCSI response PDU with an ExpDataSN that implies missing data PDU(s), that means the initiator must have detected a header or payload digest error on one or more earlier R2T/data PDUs ๐Ÿ•ต๏ธโ€โ™€๏ธ. the initiator MUST address these implied digest errors as described in Section 6.7 Digest Errors. when a target receives a data PDU with an out of order DataSN, the target must have hit a header or payload digest error on at least one of the earlier data PDUs. the target MUST address these implied digest errors as described in Section 6.7 Digest Errors.

when an initiator receives an iSCSI status PDU with an out of order StatSN that implies missing responses, it MUST address the one or more missing status PDUs as described in Section 6.7 Digest Errors ๐Ÿ™Œ. as a side effect of receiving the missing responses, the initiator may discover missing data PDUs. if the initiator wants to recover the missing data for a command, it MUST NOT acknowledge the received responses that start from the StatSN of the relevant command, until it has completed receiving all the data PDUs of the command ๐Ÿ›‘

when an initiator receives duplicate R2TSNs (due to proactive retransmission of R2Ts by the target) or duplicate DataSNs (due to proactive SNACKs by the initiator), it MUST discard the duplicates ๐Ÿงน

6.9. SCSI Timeouts โฐ

an iSCSI initiator MAY try to plug a command sequence gap on the target end before the ULP timeout ๐Ÿช„ (when there's no acknowledgement of the command by way of ExpCmdSN) by retrying the unacknowledged command, as described in Section 6.2 Retry and Reassign in Recovery.

on a ULP timeout for a command (that carried a CmdSN of n), if the iSCSI initiator wants to keep the session going, it MUST abort the command. it can do that either with an appropriate Task Management function request for the specific command, or with a "close the connection" Logout ๐Ÿ’…. when using an ABORT TASK, if the ExpCmdSN is still less than (n+1), the target may see the abort request while missing the original command itself, for one of these reasons:

  • Original command was dropped due to digest error.
  • Connection on which the original command was sent was successfully logged out. Upon logout, the unacknowledged commands issued on the connection being logged out are discarded.

if the abort request is received and the original command is missing, targets MUST consider the original command with that RefCmdSN to be received and issue a Task Management response with the response code: Function Complete. this response concludes the task on both ends ๐ŸŽ‰. if the abort request is received and the target can determine (based on the Referenced Task Tag) that the command was received and executed, and also that the response was sent prior to the abort, then the target MUST respond with the response code of Task Does Not Exist ๐Ÿซถ

6.10. Negotiation Failures ๐Ÿ’…

okay so when you send text requests and responses to set or negotiate operational parameters, that back-and-forth IS the negotiation ๐Ÿซถ and a negotiation failure is basically any of these (ngl they're all kinda messy):

  • None of the choices, or the stated value, is acceptable to one of the sides in the negotiation.
  • The text request timed out and possibly terminated.
  • The text request was answered with a Reject PDU.

there are two rules for handling these, and they're lowkey different depending on where you are ๐Ÿฆ‹

  • During Login, any failure in negotiation MUST be considered a login process failure and the Login Phase must be terminated, and with it, the connection. If the target detects the failure, it must terminate the login with the appropriate Login Response code. no cap, login failures are a hard stop ๐Ÿ›‘
  • A failure in negotiation, while in the Full Feature Phase, will terminate the entire negotiation sequence that may consist of a series of text requests that use the same Initiator Task Tag. The operational parameters of the session or the connection MUST continue to be the values agreed upon during an earlier successful negotiation (i.e., any partial results of this unsuccessful negotiation MUST NOT take effect and MUST be discarded). so the half-finished stuff just gets tossed, and your last successful settings stay put ๐Ÿงšโœจ

6.11. Protocol Errors ๐ŸŒธ

okay so mapping framed messages over a "stream" connection, like TCP, makes the proposed mechanisms kinda vulnerable to simple software framing errors ๐Ÿ˜ญ BUT adding framing mechanisms to limit those effects might be onerous on performance for simple implementations, so it's a tradeoff babe. Command Sequence Numbers and the mechanisms for connection drop and reestablishment (from earlier!) help handle this type of mapping error ๐ŸŽ€

all violations of iSCSI PDU exchange sequences specified in this document are also protocol errors. this category can ONLY be addressed by fixing the implementations, and iSCSI defines Reject and response codes to enable that ๐Ÿ’€ so it's really on the devs to clean it up.


6.12. Connection Failures ๐Ÿฆ‹

iSCSI can keep a session going as long as it can keep or establish at least one TCP connection between the initiator and the target in a timely fashion. targets and/or initiators may notice a failing connection through transport level means (TCP), a gap in the command sequence number, a response stream that isn't filled for a long time, or a failing iSCSI NOP (acting as a ping) ๐Ÿ‘€ the NOP one MAY be used periodically to boost the speed and likelihood of catching connection failures. and initiators and targets MAY also use the keep-alive option on the TCP connection to enable early link failure detection on otherwise idle links ๐ŸŒˆ

on connection failure, the initiator and target MUST do one of the following:

  • Attempt connection recovery within the session (Section 6.1.4.3 Connection Recovery).
  • Logout the connection with the reason code "closes the connection" (Section 10.14.5 Implicit termination of tasks), re-issue missing commands, and implicitly terminate all active commands. this option requires support for the within-connection recovery class (Section 6.1.4.2 Recovery Within-connection).
  • Perform session recovery (Section 6.1.4.4 Session Recovery).

either side may choose to escalate to session recovery (via the initiator dropping all the connections, or via an Async Message that announces the similar intent from a target), and the other side MUST give it precedence ๐Ÿ‘‘ no arguing, the escalation wins. and on a connection failure, a target MUST terminate and/or discard all of the active immediate commands regardless of which of the above options is used (i.e., immediate commands are not recoverable across connection failures). so immediate commands are a no-recovery zone, periodt ๐Ÿ™ˆ


6.13. Session Errors ๐Ÿงš

if all of the connections of a session fail and can't be reestablished in a short time, or if initiators detect protocol errors repeatedly, an initiator may choose to terminate a session and establish a new one. it's giving "start fresh" energy โœจ

in this case, the initiator takes the following actions:

  • Resets or closes all the transport connections.
  • Terminates all outstanding requests with an appropriate response before initiating a new session. If the same I_T nexus is intended to be reestablished, the initiator MUST employ session reinstatement (see section 5.3.5).

when the session timeout (the connection state timeout for the last failed connection) happens on the target, it takes the following actions:

  • Resets or closes the TCP connections (closes the session).
  • Terminates all active tasks that were allegiant to the connection(s) that constituted the session.

and a target MUST also be prepared to handle a session reinstatement request from the initiator, which may be addressing session errors ๐Ÿ’œ


7. State Transitions ๐ŸŒˆ

okay this part is the state machine era โœจ iSCSI connections and iSCSI sessions go through several well-defined states from the time they're created to the time they're cleared. so it's like a whole life cycle, lowkey iconic ๐Ÿ’–

the connection state transitions are described in two separate but dependent state diagrams, for ease in understanding. the first one, the "standard connection state diagram", describes connection state transitions when the iSCSI connection is NOT waiting for, or undergoing, a cleanup by way of an explicit or implicit Logout. the second, the "connection cleanup state diagram", describes connection state transitions while performing the iSCSI connection cleanup ๐Ÿงน

the "session state diagram" describes the state transitions an iSCSI session goes through during its lifetime, and it depends on the states of possibly multiple iSCSI connections that participate in the session. ๐Ÿซง

states and state transitions are described in the text, tables and diagrams. the diagrams are used for illustration. the text and the tables are the governing specification, so if the pictures and words ever disagree, the words win ๐Ÿ™ˆ (don't @ me)


7.1. Standard Connection State Diagrams ๐ŸŽ€

7.1.1. State Descriptions for Initiators and Targets ๐Ÿ’…

state descriptions for the standard connection state diagram are as follows, bestie:

  • S1: FREE
    • initiator: State on instantiation, or after successful connection closure.
    • target: State on instantiation, or after successful connection closure.
  • S2: XPT_WAIT
    • initiator: Waiting for a response to its transport connection establishment request.
    • target: Illegal
  • S3: XPT_UP
    • initiator: Illegal
    • target: Waiting for the Login process to commence.
  • S4: IN_LOGIN
    • initiator: Waiting for the Login process to conclude, possibly involving several PDU exchanges.
    • target: Waiting for the Login process to conclude, possibly involving several PDU exchanges.
  • S5: LOGGED_IN
    • initiator: In Full Feature Phase, waiting for all internal, iSCSI, and transport events.
    • target: In Full Feature Phase, waiting for all internal, iSCSI, and transport events.
  • S6: IN_LOGOUT
    • initiator: Waiting for a Logout response.
    • target: Waiting for an internal event signaling completion of logout processing.
  • S7: LOGOUT_REQUESTED
    • initiator: Waiting for an internal event signaling readiness to proceed with Logout.
    • target: Waiting for the Logout process to start after having requested a Logout via an Async Message.
  • S8: CLEANUP_WAIT
    • initiator: Waiting for the context and/or resources to initiate the cleanup processing for this CSM.
    • target: Waiting for the cleanup process to start for this CSM.

7.1.2. State Transition Descriptions for Initiators and Targets

Okay bestie, transitions time ๐Ÿฆ‹ Each one is a little event that nudges a connection from one state to another, and it hits differently depending on whether you're the initiator or the target ๐Ÿ’…

  • T1 ๐ŸŒธ
    • initiator: A transport connect request was made (e.g., TCP SYN sent).
    • target: Illegal ๐Ÿ™…
  • T2 ๐Ÿ’€
    • initiator: The transport connection request timed out, a transport reset was received, or an internal event of receiving a Logout response (success) on another connection for a "close the session" Logout request was received.
    • target: Illegal ๐Ÿ™…
  • T3 ๐Ÿงš
    • initiator: Illegal ๐Ÿ™…
    • target: A valid transport connection request was received, and that establishes the transport connection.
  • T4 โœจ
    • initiator: The transport connection got established, so the initiator is prompted to start the iSCSI Login.
    • target: The initial iSCSI Login Request was received.
  • T5 ๐ŸŽ€
    • initiator: The final iSCSI Login Response with a Status-Class of zero was received.
    • target: The final iSCSI Login Request to conclude the Login Phase was received, so the target sends the final iSCSI Login Response with a Status-Class of zero.
  • T6 ๐Ÿ˜ญ
    • initiator: Illegal ๐Ÿ™…
    • target: The target timed out waiting for an iSCSI Login, a transport disconnect indication was received, a transport reset was received, or an internal event indicating a transport timeout was received. In all of these cases, the connection is to be closed.
  • T7 ๐Ÿฅ€
    • initiator: One of the following events caused the transition:
      • The final iSCSI Login Response was received with a non-zero Status-Class.
      • Login timed out.
      • A transport disconnect indication was received.
      • A transport reset was received.
      • An internal event was received indicating a transport timeout.
      • An internal event of receiving a Logout response (success) on another connection for a "close the session" Logout request was received.

In all of these cases, the transport connection is closed.

  • target: One of the following events caused the transition:
    • The final iSCSI Login Request to conclude the Login Phase was received, prompting the target to send the final iSCSI Login Response with a non-zero Status-Class.
    • Login timed out.
    • A transport disconnect indication was received.
    • A transport reset was received.
    • An internal event indicating a transport timeout was received.
    • On another connection, a "close the session" Logout request was received.

In all of these cases, the connection is to be closed.

  • T8 ๐Ÿ“
    • initiator: An internal event of receiving a Logout response (success) on another connection for a "close the session" Logout request was received, which closes this connection and requires no further cleanup.
    • target: An internal event of sending a Logout response (success) on another connection for a "close the session" Logout request was received, or an internal event of a successful connection/session reinstatement was received, so the target closes this connection cleanly.
  • T9, T10 ๐Ÿ”ฅ
    • initiator: An internal event that indicates readiness to start the Logout process was received, so the initiator sends an iSCSI Logout.
    • target: An iSCSI Logout request was received.
  • T11, T12 ๐ŸŒˆ
    • initiator: An Async PDU with AsyncEvent "Request Logout" was received.
    • target: An internal event that requires decommissioning of the connection was received, so an Async PDU with an AsyncEvent "Request Logout" gets sent.
  • T13 ๐Ÿ‘‘
    • initiator: An iSCSI Logout response (success) was received, or an internal event of receiving a Logout response (success) on another connection for a "close the session" Logout request was received.
    • target: An internal event that indicates successful processing of the Logout was received, which prompts an iSCSI Logout response (success) to be sent. Alternatively, an internal event of sending a Logout response (success) on another connection for a "close the session" Logout request was received, or an internal event of a successful connection/session reinstatement was received.

In all of these cases, the transport connection is closed.

  • T14 ๐Ÿ™ˆ
    • initiator: An Async PDU with AsyncEvent "Request Logout" was received again.
    • target: Illegal ๐Ÿ™…
  • T15, T16 ๐Ÿ’œ
    • initiator: One or more of the following events caused this transition:
      • An internal event indicating a transport connection timeout was received, thus prompting a transport RESET or transport connection closure.
      • A transport RESET.
      • A transport disconnect indication.
      • An Async PDU with AsyncEvent "Drop connection" (for this CID).
      • An Async PDU with AsyncEvent "Drop all connections".
    • target: One or more of the following events caused this transition:
      • An internal event indicating a transport connection timeout was received, thus prompting a transport RESET or transport connection closure.
      • An internal event of a failed connection/session reinstatement was received.
      • A transport RESET.
      • A transport disconnect indication.
      • An internal emergency cleanup event was received, which prompts an Async PDU with AsyncEvent "Drop connection" (for this CID), or the event "Drop all connections".
  • T17 ๐ŸŒธ
    • initiator: One or more of the following events caused this transition:
      • A Logout response (failure, i.e., a non-zero status) was received, or Logout timed out.
      • Any of the events specified for T15 and T16.
    • target: One or more of the following events caused this transition:
      • An internal event indicating a failure of the Logout processing was received, which prompts a Logout response (failure, i.e., a non-zero status) to be sent.
      • Any of the events specified for T15 and T16.
  • T18 โญ
    • initiator: An internal event of receiving a Logout response (success) on another connection for a "close the session" Logout request was received.
    • target: An internal event of sending a Logout response (success) on another connection for a "close the session" Logout request was received, or an internal event of a successful connection/session reinstatement was received.

In both of these cases, the connection is closed.

The CLEANUP_WAIT state (S8) implies that there are possible iSCSI tasks that have not reached conclusion and are still considered busy. Main character energy, but make it a little messy ๐Ÿงน


7.1.3. Standard Connection State Diagram for an Initiator

Here are the symbolic names for the states, bestie ๐Ÿซถ

  • S1: FREE
  • S2: XPT_WAIT
  • S4: IN_LOGIN
  • S5: LOGGED_IN
  • S6: IN_LOGOUT
  • S7: LOGOUT_REQUESTED
  • S8: CLEANUP_WAIT

States S5, S6, and S7 make up the Full Feature Phase operation of the connection, so that's the main event ๐Ÿ‘‘

Now the diagram. It's a lot, but it's giving art ๐ŸŽจ

                 -------<-------------+
     +--------->/ S1    \<----+       |
  T13|       +->\       /<-+   \      |
     |      /    ---+---    \   \     |
     |     /        |     T2 \   |    |
     |  T8 |        |T1       |  |    |
     |     |        |        /   |T7  |
     |     |        |       /    |    |
     |     |        |      /     |    |
     |     |        V     /     /     |
     |     |     ------- /     /      |
     |     |    / S2    \     /       |
     |     |    \       /    /        |
     |     |     ---+---    /         |
     |     |        |T4    /          |
     |     |        V     /           | T18
     |     |     ------- /            |
     |     |    / S4    \             |
     |     |    \       /             |
     |     |     ---+---              |         T15
     |     |        |T5      +--------+---------+
     |     |        |       /T16+-----+------+  |
     |     |        |      /   -+-----+--+   |  |
     |     |        |     /   /  S7   \  |T12|  |
     |     |        |    / +->\       /<-+   V  V
     |     |        |   / /    -+-----       -------
     |     |        |  / /T11   |T10        /  S8   \
     |     |        V / /       V  +----+   \       /
     |     |      ---+-+-      ----+--  |    -------
     |     |     / S5    \T9  / S6    \<+    ^
     |     +-----\       /--->\       / T14  |
     |            -------      --+----+------+T17
     +---------------------------+

Next up is the state transition table, which shows the same diagram in grid form. Read each row as the starting state, and whichever transition is in a cell takes you to the state in that column. For example, from S1 you take T1 to land in S2. A - means the transition is undefined ๐Ÿ™ƒ

      +----+---+---+---+---+----+---+
      |S1  |S2 |S4 |S5 |S6 |S7  |S8 |
   ---+----+---+---+---+---+----+---+
    S1| -  |T1 | - | - | - | -  | - |
   ---+----+---+---+---+---+----+---+
    S2|T2  |-  |T4 | - | - | -  | - |
   ---+----+---+---+---+---+----+---+
    S4|T7  |-  |-  |T5 | - | -  | - |
   ---+----+---+---+---+---+----+---+
    S5|T8  |-  |-  | - |T9 |T11 |T15|
   ---+----+---+---+---+---+----+---+
    S6|T13 |-  |-  | - |T14|-   |T17|
   ---+----+---+---+---+---+----+---+
    S7|T18 |-  |-  | - |T10|T12 |T16|
   ---+----+---+---+---+---+----+---+
    S8| -  |-  |-  | - | - | -  | - |
   ---+----+---+---+---+---+----+---+

7.1.4. Standard Connection State Diagram for a Target โœจ

okay so here are the target's connection states, bestie ๐ŸŽ€ they're literally the same vibe as the initiator's, just with targets doing the driving ๐Ÿ’…

Symbolic names for States:

S1: FREE
S3: XPT_UP
S4: IN_LOGIN
S5: LOGGED_IN
S6: IN_LOGOUT
S7: LOGOUT_REQUESTED
S8: CLEANUP_WAIT

States S5, S6, and S7 make up the Full Feature Phase of the connection ๐ŸŒธ that's where all the real action happens!

The state diagram looks like this ๐Ÿฆ‹

                    -------<-------------+
        +--------->/ S1    \<----+       |
     T13|       +->\       /<-+   \      |
        |      /    ---+---    \   \     |
        |     /        |     T6 \   |    |
        |  T8 |        |T3       |  |    |
        |     |        |        /   |T7  |
        |     |        |       /    |    |
        |     |        |      /     |    |
        |     |        V     /     /     |
        |     |     ------- /     /      |
        |     |    / S3    \     /       |
        |     |    \       /    /        | T18
        |     |     ---+---    /         |
        |     |        |T4    /          |
        |     |        V     /           |
        |     |     ------- /            |
        |     |    / S4    \             |
        |     |    \       /             |
        |     |     ---+---         T15  |
        |     |        |T5      +--------+---------+
        |     |        |       /T16+-----+------+  |
        |     |        |      /  -+-----+---+   |  |
        |     |        |     /   /  S7   \  |T12|  |
        |     |        |    / +->\       /<-+   V  V
        |     |        |   / /    -+-----       -------
        |     |        |  / /T11   |T10        /  S8   \
        |     |        V / /       V           \       /
        |     |      ---+-+-      -------       -------
        |     |     / S5    \T9  / S6    \        ^
        |     +-----\       /--->\       /        |
        |            -------      --+----+--------+T17
        +---------------------------+

Here's the state transition table that matches the diagram. It follows the same conventions as the initiator diagram ๐Ÿ’–

   +----+---+---+---+---+----+---+
   |S1  |S3 |S4 |S5 |S6 |S7  |S8 |
---+----+---+---+---+---+----+---+
 S1| -  |T3 | - | - | - | -  | - |
---+----+---+---+---+---+----+---+
 S3|T6  |-  |T4 | - | - | -  | - |
---+----+---+---+---+---+----+---+
 S4|T7  |-  |-  |T5 | - | -  | - |
---+----+---+---+---+---+----+---+
 S5|T8  |-  |-  | - |T9 |T11 |T15|
---+----+---+---+---+---+----+---+
 S6|T13 |-  |-  | - |-  |-   |T17|
---+----+---+---+---+---+----+---+
 S7|T18 |-  |-  | - |T10|T12 |T16|
---+----+---+---+---+---+----+---+
 S8| -  |-  |-  | - | - | -  | - |
---+----+---+---+---+---+----+---+

7.2. Connection Cleanup State Diagram for Initiators and Targets ๐Ÿงน

Symbolic names for states:

R1: CLEANUP_WAIT (same as S8)
R2: IN_CLEANUP
R3: FREE (same as S1)

okay this part is a little bit of a plot twist ๐Ÿซฃ whenever a connection state machine (for example CSM-C) lands in the CLEANUP_WAIT state (S8), it must go through the transitions in the connection cleanup state diagram. It can do that in one of two ways:

  • a) using a separate full-feature phase connection (let's call it CSM-E) that's in the LOGGED_IN state in the same session, or
  • b) using a new transport connection (let's call it CSM-I) that's in the FREE state and gets added to the same session.

In the CSM-E case, an explicit logout for the CID that corresponds to CSM-C (either as a connection or session logout) needs to be performed to finish the cleanup. In the CSM-I case, an implicit logout for the CID that corresponds to CSM-C needs to be performed by way of connection reinstatement (section 5.3.4) for that CID. Either way, the protocol exchanges happening on CSM-E or CSM-I are what decide the state transitions for CSM-C. So this cleanup diagram only applies to the one connection that's being cleaned up (that's CSM-C) ๐ŸŽฏ

For the implicit logout, CSM-C reaches FREE (R3) at the moment CSM-I reaches LOGGED_IN. For the explicit logout, CSM-C reaches FREE (R3) when CSM-E gets a successful logout response while it's still sitting in the LOGGED_IN state.

An initiator must start an explicit or implicit connection logout for a connection in the CLEANUP_WAIT state, if the initiator plans on continuing to use the associated iSCSI session ๐Ÿ™Œ

The following state diagram works for both initiators and targets โœจ

                    -------
                   / R1    \
                +--\       /<-+
               /    ---+---
              /        |        \ M3
           M1 |        |M2       |
              |        |        /
              |        |       /
              |        |      /
              |        V     /
              |     ------- /
              |    / R2    \
              |    \       /
              |     -------
              |        |
              |        |M4
              |        |
              |        |
              |        |
              |        V
              |      -------
              |     / R3    \
              +---->\       /
                     -------

Here's the matching state transition table. It follows the same conventions as in the earlier sections ๐Ÿ’œ

    +----+----+----+
    |R1  |R2  |R3  |
-----+----+----+----+
 R1  | -  |M2  |M1  |
-----+----+----+----+
 R2  |M3  | -  |M4  |
-----+----+----+----+
 R3  | -  | -  | -  |
-----+----+----+----+

7.2.1. State Descriptions for Initiators and Targets ๐Ÿง 

  • R1: CLEANUP_WAIT (Same as S8)
    • initiator: Waiting for the internal event to start the cleanup processing for CSM-C.
    • target: Waiting for the cleanup process to start for CSM-C.
  • R2: IN_CLEANUP
    • initiator: Waiting for the connection cleanup process to conclude for CSM-C.
    • target: Waiting for the connection cleanup process to conclude for CSM-C.
  • R3: FREE (Same as S1)
    • initiator: End state for CSM-C.
    • target: End state for CSM-C.

7.2.2. State Transition Descriptions for Initiators and Targets ๐Ÿฆ‹

  • M1: One or more of the following events was received:
    • initiator:
      • An internal event that indicates a connection state timeout.
      • An internal event of receiving a successful Logout response on a different connection for a "close the session" Logout.
    • target:
      • An internal event that indicates a connection state timeout.
      • An internal event of sending a Logout response (success) on a different connection for a "close the session" Logout request.
  • M2: An implicit or explicit logout process was initiated by the initiator.
    • In CSM-I usage:
      • initiator: An internal event requesting the connection (or session) reinstatement was received, which prompted a connection (or session) reinstatement Login to be sent, moving CSM-I into state IN_LOGIN.
      • target: A connection or session reinstatement Login was received while in state XPT_UP.
    • In CSM-E usage:
      • initiator: An internal event indicating that an explicit logout was sent for this CID in state LOGGED_IN.
      • target: An explicit logout was received for this CID in state LOGGED_IN.
  • M3: Logout failure detected ๐Ÿ˜ฌ
    • In CSM-I usage:
      • initiator: CSM-I failed to reach LOGGED_IN and arrived in FREE instead.
      • target: CSM-I failed to reach LOGGED_IN and arrived in FREE instead.
    • In CSM-E usage:
      • initiator: CSM-E either moved out of LOGGED_IN, or the Logout timed out and/or was aborted, or a Logout response (failure) was received.
      • target: CSM-E either moved out of LOGGED_IN, the Logout timed out and/or was aborted, or an internal event indicating that a failed Logout processing was received. A Logout response (failure) was sent in the last case.
  • M4: A successful implicit or explicit logout was performed. ๐ŸŽ‰
    • In CSM-I usage:
      • initiator: CSM-I reached state LOGGED_IN, or an internal event of receiving a Logout response (success) on another connection for a "close the session" Logout request was received.
      • target: CSM-I reached state LOGGED_IN, or an internal event of sending a Logout response (success) on a different connection for a "close the session" Logout request was received.
    • In CSM-E usage:
      • initiator: CSM-E stayed in LOGGED_IN and received a Logout response (success), or an internal event of receiving a Logout response (success) on another connection for a "close the session" Logout request was received.
      • target: CSM-E stayed in LOGGED_IN and an internal event indicating successful Logout processing was received, or an internal event of sending a Logout response (success) on a different connection for a "close the session" Logout request was received.

7.3. Session State Diagrams ๐ŸŒˆ

7.3.1. Session State Diagram for an Initiator ๐Ÿ‘‘

Symbolic Names for States:

Q1: FREE
Q3: LOGGED_IN
Q4: FAILED

State Q3 is the Full Feature Phase operation of the session โœจ

The state diagram looks like this:

                    -------
                   / Q1    \
           +------>\       /<-+
          /         ---+---   |
         /             |      |N3
     N6 |              |N1    |
        |              |      |
        |    N4        |      |
        |  +--------+  |     /
        |  |        |  |    /
        |  |        |  |   /
        |  |        V  V  /
       -+--+--      -----+-
      / Q4    \ N5 / Q3    \
      \       /<---\       /
       -------      -------

The state transition table is as follows:

    +----+----+----+
    |Q1  |Q3  |Q4  |
-----+----+----+----+
 Q1  | -  |N1  | -  |
-----+----+----+----+
 Q3  |N3  | -  |N5  |
-----+----+----+----+
 Q4  |N6  |N4  | -  |
-----+----+----+----+

7.3.2. Session State Diagram for a Target ๐ŸŽฏ

Okay bestie, this one is the session state machine from the target's side ๐ŸŽ€ Here are the symbolic names first:

  • Q1: FREE
  • Q2: ACTIVE
  • Q3: LOGGED_IN
  • Q4: FAILED
  • Q5: IN_CONTINUE

Q3 is the main-character era of the session ๐Ÿ‘‘ It's where the Full Feature Phase operation lives.

Here's the diagram, no cap it's kind of a lot ๐Ÿงš

                                    -------
               +------------------>/ Q1    \
              /    +-------------->\       /<-+
              |    |                ---+---   |
              |    |                ^  |      |N3
           N6 |    |N11           N9|  V N1   |
              |    |                +------   |
              |    |               / Q2    \  |
              |    |               \       /  |
              |  --+----            +--+---   |
              | / Q5    \              |      |
              | \       / N10          |      |
              |  +-+---+------------+  |N2   /
              |  ^ |                |  |    /
              |N7| |N8              |  |   /
              |  | |                |  V  /
             -+--+-V                V----+-
            / Q4    \ N5           / Q3    \
            \       /<-------------\       /
             -------                -------

And here's the transition table, all laid out ๐Ÿ’…

        +----+----+----+----+----+
        |Q1  |Q2  |Q3  |Q4  |Q5  |
   -----+----+----+----+----+----+
    Q1  | -  |N1  | -  | -  | -  |
   -----+----+----+----+----+----+
    Q2  |N9  | -  |N2  | -  | -  |
   -----+----+----+----+----+----+
    Q3  |N3  | -  | -  |N5  | -  |
   -----+----+----+----+----+----+
    Q4  |N6  | -  | -  | -  |N7  |
   -----+----+----+----+----+----+
    Q5  |N11 | -  |N10 |N8  | -  |
   -----+----+----+----+----+----+

7.3.3. State Descriptions for Initiators and Targets ๐Ÿ’œ

Here's what each state means for each side ๐Ÿฆ‹

  • Q1: FREE
    • initiator: This is where things start on instantiation, or after cleanup.
    • target: Same vibe, the target starts here on instantiation or after cleanup.
  • Q2: ACTIVE
    • initiator: Illegal.
    • target: The first iSCSI connection in the session has moved to IN_LOGIN, and the target is waiting for that connection to finish logging in.
  • Q3: LOGGED_IN
    • initiator: Waiting for all session events.
    • target: Waiting for all session events.
  • Q4: FAILED
    • initiator: Waiting for session recovery or session continuation.
    • target: Waiting for session recovery or session continuation.
  • Q5: IN_CONTINUE
    • initiator: Illegal.
    • target: Waiting for a session continuation attempt to reach a conclusion.

7.3.4. State Transition Descriptions for Initiators and Targets โœจ

Now the plot twists ๐ŸŒˆ Each N is an event that moves the session around.

  • N1
    • initiator: At least one transport connection has reached the LOGGED_IN state.
    • target: The first iSCSI connection in the session had reached the IN_LOGIN state.
  • N2
    • initiator: Illegal.
    • target: At least one iSCSI connection has reached the LOGGED_IN state.
  • N3
    • initiator: The session is closed gracefully through session closure (Section 5.3.6 Session Continuation and Failure).
    • target: The session is closed gracefully through session closure (Section 5.3.6 Session Continuation and Failure), or a successful session reinstatement cleanly closed the session.
  • N4
    • initiator: A session continuation attempt succeeded.
    • target: Illegal.
  • N5
    • initiator: Session failure (Section 5.3.6 Session Continuation and Failure) occurred.
    • target: Session failure (Section 5.3.6 Session Continuation and Failure) occurred.
  • N6
    • initiator: A session state timeout occurred, or a session reinstatement cleared this session instance. This frees all the associated resources, and the session state is discarded ๐Ÿฅ€
    • target: A session state timeout occurred, or a session reinstatement cleared this session instance. This frees all the associated resources, and the session state is discarded ๐Ÿฅ€
  • N7
    • initiator: Illegal.
    • target: A session continuation attempt is initiated.
  • N8
    • initiator: Illegal.
    • target: The last session continuation attempt failed.
  • N9
    • initiator: Illegal.
    • target: The login attempt on the leading connection failed.
  • N10
    • initiator: Illegal.
    • target: A session continuation attempt succeeded.
  • N11
    • initiator: Illegal.
    • target: A successful session reinstatement cleanly closed the session.

8. Security Considerations ๐Ÿ”

Okay so historically, native storage systems never really had to stress about security ๐Ÿ™ˆ Their environments were pretty chill on risk, since the storage devices were either plugged straight into hosts or sitting on a SAN that was totally separate from the regular communications network. Once storage protocols like SCSI start running over IP networks, though, security suddenly has to be a whole priority ๐Ÿ˜ญ

iSCSI implementations MUST provide protection against active attacks (for example, pretending to be another identity, inserting, deleting, modifying, and replaying messages) and passive attacks (for example, eavesdropping, or gaining an advantage by analyzing the data sent over the line). This is non-negotiable bestie ๐Ÿ’€

iSCSI SHOULD NOT be configured without security. Technically it's possible, but iSCSI configured without security should be confined, in extreme cases, to closed environments that have no security risk at all. [RFC3723] specifies the mechanisms that must be used in order to fully mitigate the risks described in that document ๐Ÿšจ

Here's the rundown on the security mechanisms an iSCSI implementation provides ๐Ÿ›ก๏ธ

8.1. iSCSI Security Mechanisms ๐Ÿ›ก๏ธ

The entities involved in iSCSI security are the initiator, the target, and the IP communication end points. We expect scenarios where several initiators or targets share a single communication end point, so iSCSI uses two separate security mechanisms to handle that ๐Ÿซถ The first is in-band authentication between the initiator and the target at the iSCSI connection level, carried out by exchanging iSCSI Login PDUs. The second is packet protection (integrity, authentication, and confidentiality) by IPsec at the IP level. They complement each other perfectly โœจ In-band authentication gives end-to-end trust between the iSCSI initiator and the target at login time, while IPsec gives a secure channel between the IP communication end points.

More details on typical iSCSI scenarios, and on how initiators, targets, and communication end points relate to each other, can be found in [RFC3723].

8.2. In-band Initiator-Target Authentication ๐Ÿ”‘

During login, the target MAY authenticate the initiator, and the initiator MAY authenticate the target. This happens on every new iSCSI connection through an exchange of iSCSI Login PDUs, using a negotiated authentication method.

The authentication method can't count on IPsec being underneath it, since IPsec is optional to use ๐Ÿ™ƒ An attacker should gain as little advantage as possible from inspecting the authentication phase PDUs. So a method that uses clear text passwords (or something equivalent) is not acceptable. On the other hand, identity protection isn't strictly required.

The authentication mechanism protects against an unauthorized login to storage resources by using a false identity (spoofing). Once the authentication phase is done, if IPsec isn't being used, everything is sent and received in clear. The authentication mechanism on its own (without IPsec underneath) should only be used when there is no risk of eavesdropping, message insertion, deletion, modification, and replaying.

Section 11 iSCSI Security Text Keys and Authentication Methods defines several authentication methods and the exact steps that must be followed in each of them, including the iSCSI-text-keys and their allowed values in each step. Whenever an iSCSI initiator gets a response whose keys, or their values, don't match the step definition, it MUST abort the connection. Whenever an iSCSI target gets a response whose keys, or their values, don't match the step definition, it MUST answer with a Login reject carrying the "Initiator Error" or "Missing Parameter" status. Those statuses aren't meant for cryptographically incorrect values such as the CHAP response, for which the "Authentication Failure" status MUST be specified ๐Ÿšซ

This rule matters a lot, so here's why. In CHAP with target authentication (see Section 11.1.4 Challenge Handshake Authentication Protocol (CHAP)), the initiator could conduct a reflection attack by leaving out its response key (CHAP_R), reusing the same CHAP challenge as the target, and reflecting the target's response back at the target ๐Ÿ˜ฑ CHAP stops this because the target has to answer a missing CHAP_R key with a Login reject carrying the "Missing Parameter" status.

For some of the authentication methods, a key specifies the identity of the iSCSI initiator or target for authentication purposes. The value tied to that key MAY be different from the iSCSI name, and SHOULD be configurable. (CHAP_N, see Section 11.1.4 Challenge Handshake Authentication Protocol (CHAP), and SRP_U, see Section 11.1.3 Secure Remote Password (SRP).) ๐ŸŽ€

8.2.1. CHAP Considerations โœจ

okay bestie, CHAP is up next and it's giving serious security drama ๐Ÿ’… Compliant iSCSI initiators and targets MUST implement the CHAP authentication method [RFC1994] (according to Section 11.1.4 Challenge Handshake Authentication Protocol (CHAP) including the target authentication option). ๐Ÿ”

ngl, when CHAP runs over a channel that isn't encrypted, it's vulnerable to an off-line dictionary attack ๐Ÿ˜ญ so implementations MUST support use of up to 128 bit random CHAP secrets ๐ŸŽ€, including the means to generate those secrets and to accept them from an external generation source. And the only secret generation (or expansion) means an implementation MUST NOT provide is anything other than random generation ๐Ÿซถ

highkey, if an administrative entity is using CHAP with a secret that has less than 96 random bits, it MUST enforce IPsec encryption (according to the implementation requirements in Section 8.3.2 Confidentiality) to protect the connection ๐Ÿ›ก๏ธ Moreover, in this case IKE authentication with group pre-shared cryptographic keys SHOULD NOT be used unless it is not essential to protect group members against off-line dictionary attacks by other members. ๐Ÿ™ˆ

CHAP secrets MUST be an integral number of bytes (octets) ๐Ÿ“ A compliant implementation SHOULD NOT continue with the login step in which it should send a CHAP response (CHAP_R, Section 11.1.4 Challenge Handshake Authentication Protocol (CHAP)) unless it can verify that the CHAP secret is at least 96 bits, or that IPsec encryption is being used to protect the connection.

Now the main event ๐Ÿ‘‘ Any CHAP secret used for initiator authentication MUST NOT be configured for authentication of any target, and any CHAP secret used for target authentication MUST NOT be configured for authentication of any initiator. If the CHAP response received by one end of an iSCSI connection is the same as the CHAP response that the receiving endpoint would have generated for the same CHAP challenge, the response MUST be treated as an authentication failure and cause the connection to close (this ensures that the same CHAP secret is not used for authentication in both directions). Also, if an iSCSI implementation can function as both initiator and target, different CHAP secrets and identities MUST be configured for these two roles. ๐Ÿฆ‹

Here's the attack these rules are stopping, and it's sooo sneaky:

Rogue wants to impersonate Storage to Alice, and knows that a
single secret is used for both directions of Storage-Alice
authentication.

Rogue convinces Alice to open two connections to Rogue, and Rogue
identifies itself as Storage on both connections.

Rogue issues a CHAP challenge on connection 1, waits for Alice to
respond, and then reflects Alice's challenge as the initial
challenge to Alice on connection 2.

If Alice doesn't check for the reflection across connections,
Alice's response on connection 2 enables Rogue to impersonate
Storage on connection 1, even though Rogue does not know the
Alice-Storage CHAP secret.

Originators MUST NOT reuse the CHAP challenge sent by the Responder for the other direction of a bidirectional authentication. Responders MUST check for this condition and close the iSCSI TCP connection if it occurs. ๐Ÿšซ

The same CHAP secret SHOULD NOT be configured for authentication of multiple initiators or multiple targets, because that lets any of them impersonate any other one of them, and if one gets compromised, the attacker can impersonate any of them ๐Ÿ˜ฌ It is recommended that iSCSI implementations check for use of identical CHAP secrets by different peers when this check is feasible, and take appropriate measures to warn users and/or administrators when this is detected. ๐Ÿ””

When an iSCSI initiator or target authenticates itself to counterparts in multiple administrative domains, it SHOULD use a different CHAP secret for each administrative domain to avoid propagating security compromises across domains. ๐ŸŒˆ

Within a single administrative domain, the rules are a bit more chill:

  • A single CHAP secret MAY be used for authentication of an initiator to multiple targets.
  • A single CHAP secret MAY be used for an authentication of a target to multiple initiators when the initiators use an external server (e.g., RADIUS) to verify the target's CHAP responses and do not know the target's CHAP secret.

If an external response verification server (e.g., RADIUS) is not used, employing a single CHAP secret for authentication of a target to multiple initiators requires that all such initiators know that target secret. Any of these initiators can impersonate the target to any other such initiator, and compromise of such an initiator enables an attacker to impersonate the target to all such initiators. Targets SHOULD use separate CHAP secrets for authentication to each initiator when such risks are of concern; in this situation it may be useful to configure a separate logical iSCSI target with its own iSCSI Node Name for each initiator or group of initiators among which such separation is desired. ๐Ÿงš

8.2.2. SRP Considerations ๐Ÿ’œ

The strength of the SRP authentication method (specified in [RFC2945]) depends on the characteristics of the group being used (i.e., the prime modulus N and generator g). As described in [RFC2945], N is required to be a Sophie-German prime (of the form N = 2q + 1, where q is also prime) and the generator g is a primitive root of GF(n). In iSCSI authentication, the prime modulus N MUST be at least 768 bits. ๐Ÿ”ข

The list of allowed SRP groups is provided in [RFC3723]. ๐Ÿ“œ

8.3. IPsec ๐ŸŒธ

iSCSI uses the IPsec mechanism for packet protection (cryptographic integrity, authentication, and confidentiality) at the IP level between the iSCSI communicating end points. The following sections describe the IPsec protocols that must be implemented for data integrity and authentication, confidentiality, and cryptographic key management. ๐Ÿ”

An iSCSI initiator or target may provide the required IPsec support fully integrated or in conjunction with an IPsec front-end device. In the latter case, the compliance requirements with regard to IPsec support apply to the "combined device". Only the "combined device" is to be considered an iSCSI device. ๐ŸŽ€

Detailed considerations and recommendations for using IPsec for iSCSI are provided in [RFC3723]. ๐Ÿ“–

8.3.1. Data Integrity and Authentication ๐Ÿฆ‹

Data authentication and integrity is provided by a cryptographic keyed Message Authentication Code in every sent packet. This code protects against message insertion, deletion, and modification. Protection against message replay is realized by using a sequence counter. ๐Ÿ”

An iSCSI compliant initiator or target MUST provide data integrity and authentication by implementing IPsec [RFC2401] with ESP [RFC2406] in tunnel mode and MAY provide data integrity and authentication by implementing IPsec with ESP in transport mode. The IPsec implementation MUST fulfill the following iSCSI specific requirements:

  • HMAC-SHA1 MUST be implemented [RFC2404].
  • AES CBC MAC with XCBC extensions SHOULD be implemented [RFC3566].

The ESP anti-replay service MUST also be implemented. ๐Ÿ›ก๏ธ

At the high speeds iSCSI is expected to operate, a single IPsec SA could rapidly cycle through the 32-bit IPsec sequence number space ๐Ÿ˜ณ In view of this, it may be desirable in the future for an iSCSI implementation that operates at speeds of 1 Gbps or greater to implement the IPsec sequence number extension [SEQ-EXT]. ๐Ÿš€

8.3.2. Confidentiality ๐Ÿคซ

Confidentiality is provided by encrypting the data in every packet. When confidentiality is used it MUST be accompanied by data integrity and authentication to provide comprehensive protection against eavesdropping, message insertion, deletion, modification, and replaying. ๐Ÿ”’

An iSCSI compliant initiator or target MUST provide confidentiality by implementing IPsec [RFC2401] with ESP [RFC2406] in tunnel mode and MAY provide confidentiality by implementing IPsec with ESP in transport mode, with the following iSCSI specific requirements:

  • 3DES in CBC mode MUST be implemented [RFC2451].
  • AES in Counter mode SHOULD be implemented [RFC3686].

DES in CBC mode SHOULD NOT be used due to its inherent weakness. The NULL encryption algorithm MUST also be implemented. ๐Ÿ’€

8.3.3. Policy, Security Associations, and Cryptographic Key Management ๐Ÿ‘‘

A compliant iSCSI implementation MUST meet the cryptographic key management requirements of the IPsec protocol suite. Authentication, security association negotiation, and cryptographic key management MUST be provided by implementing IKE [RFC2409] using the IPsec DOI [RFC2407] with the following iSCSI specific requirements:

  • Peer authentication using a pre-shared cryptographic key MUST be supported. Certificate-based peer authentication using digital signatures MAY be supported. Peer authentication using the public key encryption methods outlined in IKE sections 5.2 and 5.3[7] SHOULD NOT be used.
  • When digital signatures are used to achieve authentication, an IKE negotiator SHOULD use IKE Certificate Request Payload(s) to specify the certificate authority. IKE negotiators SHOULD check the pertinent Certificate Revocation List (CRL) before accepting a PKI certificate for use in IKE authentication procedures.
  • Conformant iSCSI implementations MUST support IKE Main Mode and SHOULD support Aggressive Mode. IKE main mode with pre-shared key authentication method SHOULD NOT be used when either the initiator or the target uses dynamically assigned IP addresses. While in many cases pre-shared keys offer good security, situations in which dynamically assigned addresses are used force the use of a group pre-shared key, which creates vulnerability to a man-in-the-middle attack. ๐Ÿ•ต๏ธ
  • In the IKE Phase 2 Quick Mode, exchanges for creating the Phase 2 SA, the Identity Payload, fields MUST be present. ID_IPV4_ADDR, ID_IPV6_ADDR (if the protocol stack supports IPv6) and ID_FQDN Identity payloads MUST be supported; ID_USER_FQDN SHOULD be supported. The IP Subnet, IP Address Range, ID_DER_ASN1_DN, and ID_DER_ASN1_GN formats SHOULD NOT be used. The ID_KEY_ID Identity Payload MUST NOT be used. ๐Ÿชช

Manual cryptographic keying MUST NOT be used because it does not provide the necessary re-keying support. ๐Ÿ”‘

When IPsec is used, the receipt of an IKE Phase 2 delete message SHOULD NOT be interpreted as a reason for tearing down the iSCSI TCP connection. If additional traffic is sent on it, a new IKE Phase 2 SA will be created to protect it. ๐Ÿ”„

The method used by the initiator to determine whether the target should be connected using IPsec is regarded as an issue of IPsec policy administration, and thus not defined in the iSCSI standard. ๐Ÿ“‹

If an iSCSI target is discovered via a SendTargets request in a discovery session not using IPsec, the initiator should assume that it does not need IPsec to establish a session to that target. If an iSCSI target is discovered using a discovery session that does use IPsec, the initiator SHOULD use IPsec when establishing a session to that target. โœจ

9. Notes to Implementers โœจ

okay bestie, this section is the "read this before you build" corner ๐ŸŽ€. It covers performance and reliability stuff for iSCSI. The protocol was designed to be efficient in both silicon and software ๐Ÿ’…, and the iSCSI task tag mechanism exists so Direct Data Placement (DDP, which is a DMA form ๐Ÿงš) can happen at the iSCSI level or lower.

The big guiding assumption behind the whole design is that targets are resource constrained compared to initiators ๐Ÿฅบ so keep that in mind, periodt.

Implementers are also advised to think about the implementation consequences of the iSCSI to SCSI mapping model in Section 3.4.3 Consequences of the Model. Go reread that one, it's giving foundation ๐Ÿฆ‹

9.1. Multiple Network Adapters

iSCSI lets you have multiple connections, and they don't all have to go over the same network adapter ๐ŸŒธ. If you're using several network connections with hardware support, the command-data-status allegiance to one TCP connection means there's no need to copy info across network adapters or make them cooperate. No extra drama ๐Ÿ’–

Hot take though: some task management commands may need some loose form of cooperation or replication, at least on the target ๐Ÿ‘€

9.1.1. Conservative Reuse of ISIDs

Okay, history lesson ๐Ÿ“œ. The SCSI model (and the implementations and apps built on it) has long assumed that SCSI ports are static, physical entities. Newer SCSI extensions take advantage of persistent worldwide unique names for those ports โœจ. In iSCSI, though, the SCSI initiator ports are the endpoints of dynamically created sessions, so "static and physical" doesn't really apply ๐Ÿ™ˆ. Anyway, the model clauses (especially Section 3.4.2 SCSI Architecture Model) allow for persistent, reusable names for iSCSI-type SCSI initiator ports, even though there doesn't need to be any physical entity bound to those names ๐Ÿ’œ.

To keep legacy apps from getting disrupted and to make the SCSI features that rely on persistent port names work better, iSCSI implementations SHOULD attempt to provide a stable presentation of SCSI Initiator Ports (both to the upper OS layers and to the targets they connect to) ๐Ÿซถ. An initiator can do this by conservatively reusing ISIDs. In other words, the same ISID should be used in the Login process to multiple target portal groups (of the same iSCSI Target or different iSCSI Targets) ๐Ÿ“.

The ISID RULE in Section 3.4.3 Consequences of the Model only prohibits reuse to the same target portal group. It does not "preclude" reuse to other target portal groups. The principle of conservative reuse "encourages" reuse to other target portal groups ๐ŸŒˆ. So when a SCSI target device sees the same (InitiatorName, ISID) pair in different sessions to different target portal groups, it can identify the underlying SCSI Initiator Port on each session as the same SCSI port. Basically, it can recognize multiple paths from the same source ๐Ÿงšโœจ.

9.1.2. iSCSI Name, ISID, and TPGT Use

The designers of iSCSI envisioned one iSCSI Initiator Node Name per operating system image on a machine ๐Ÿ’ป. This enables SAN resource configuration and authentication schemes based on a system's identity. It supports the idea that you should be able to assign access to storage resources based on "initiator device" identity ๐Ÿ”.

When there are multiple hardware or software components coordinated as a single iSCSI Node, there must be some (logical) entity that represents the iSCSI Node and makes the iSCSI Node Name available to all components involved in session creation and login ๐Ÿงฉ. Similarly, this entity that represents the iSCSI Node must be able to coordinate session identifier resources (ISID for initiators) to enforce both the ISID and TSIH RULES (see Section 3.4.3 Consequences of the Model) ๐Ÿ‘‘.

For targets, the environment is closed, so implementing this entity should be straightforward ๐ŸŒท. However, vendors of iSCSI hardware (e.g., NICs or HBAs) intended for targets SHOULD provide mechanisms for configuring the iSCSI Node Name across the portal groups instantiated by multiple instances of these components within a target ๐Ÿ› ๏ธ.

Complex targets that use multiple Target Portal Group Tags may reconfigure them to hit various quality goals ๐ŸŽฏ. Initiators have two mechanisms to discover and/or check reconfiguring targets: the discovery session type, and a key returned by the target during login to confirm the TPGT ๐Ÿ•ต๏ธโ€โ™€๏ธ. An initiator should attempt to "rediscover" the target configuration any time a session is terminated unexpectedly ๐Ÿ”„.

For initiators, long term, operating system vendors are expected to take on the role of this entity and provide standard APIs that can tell components their iSCSI Node Name and can configure and/or coordinate ISID allocation, use, and reuse ๐Ÿ–ฅ๏ธโœจ.

Those initiator APIs aren't available today, so other implementations of this role are possible ๐Ÿ’…. For example, a human may set up the (common) Node name during the installation of each iSCSI component involved in session creation and login ๐Ÿง‘โ€๐Ÿ’ป. This can be done by pointing the component to a vendor-specific location for this datum or to a system-wide location.

The structure of the ISID namespace (see Section 10.12.5 ISID and [RFC3721]) makes ISID coordination easier, because each component vendor can independently coordinate allocation, use, and reuse of its own partition of the ISID namespace in a vendor-specific manner ๐Ÿง. Partitioning the ISID namespace within initiator portal groups managed by that vendor lets each initiator portal group act independently of all the others when selecting an ISID for a login. That helps enforce the ISID RULE (see Section 3.4.3 Consequences of the Model) at the initiator ๐Ÿฆ‹.

A vendor of iSCSI hardware (e.g., NICs or HBAs) intended for use in initiators MUST implement a mechanism for configuring the iSCSI Node Name ๐Ÿ”‘. Vendors and administrators must ensure that iSCSI Node Names are unique worldwide ๐ŸŒ. So it's important that if you reuse the iSCSI Node Name of a disabled unit, you don't re-assign that name to the original unit unless its worldwide uniqueness can be confirmed again ๐Ÿ™…โ€โ™€๏ธ.

Also, a vendor of iSCSI hardware must implement a mechanism to configure and/or coordinate ISIDs for all sessions managed by multiple instances of that hardware within a given iSCSI Node ๐Ÿ’œ. That configuration might be permanently pre-assigned at the factory (in a necessarily globally unique way), statically assigned (e.g., partitioned across all the NICs at initialization in a locally unique way), or dynamically assigned (e.g., by an on-line allocator, also in a locally unique way) ๐Ÿญ. In the latter two cases, the configuration may happen via public APIs (perhaps driven by an independent vendor's software, such as the OS vendor) or via private APIs driven by the vendor's own software ๐ŸŽ€.

9.2. Autosense and Auto Contingent Allegiance (ACA)

Autosense is the automatic return of sense data to the initiator when a command didn't complete successfully ๐Ÿšจ. iSCSI initiators and targets MUST support and use autosense. No skipping this one, bestie ๐Ÿ’€.

ACA helps preserve ordered command execution in the presence of errors ๐Ÿ“‹. Since iSCSI can have lots of commands in flight between initiator and target, iSCSI initiators and targets SHOULD support ACA ๐ŸŒŸ.

9.3. iSCSI Timeouts

iSCSI recovery actions often depend on iSCSI time-outs being recognized and acted on before SCSI time-outs โฐ. Figuring out the right time-outs for various iSCSI actions (command acknowledgements expected, status acknowledgements, etc.) really depends on the infrastructure (hardware, links, TCP/IP stack, iSCSI driver) ๐Ÿง . As a guide, the implementer may use an average Nop-Out/Nop-In turnaround delay multiplied by a "safety factor" (e.g., 4) as a good estimate for the basic delay of the iSCSI stack for a given connection ๐Ÿ“. The safety factor should account for network load variability ๐ŸŒŠ. For connection teardown, the implementer may want to also consider the TCP common practice for the given infrastructure ๐Ÿงน.

Text negotiations MAY also be subject to time-limits or limits in the number of exchanges ๐Ÿ•. Those SHOULD be generous enough to avoid affecting interoperability (e.g., allowing each key to be negotiated on a separate exchange) ๐Ÿซถ.

The relation between iSCSI timeouts and SCSI timeouts also matters ๐Ÿ”—. SCSI timeouts should be longer than iSCSI timeouts plus the time required for iSCSI recovery whenever iSCSI recovery is planned ๐Ÿ•ฐ๏ธ. Alternatively, an implementer may choose to interlock iSCSI timeouts and recovery with SCSI timeouts, so SCSI recovery only becomes active where iSCSI isn't planned to recover, or failed to recover ๐Ÿ”’.

The implementer may also want to think about how various iSCSI exception events (such as a digest failure) interact with later timeouts ๐Ÿง. When iSCSI error recovery is active, a digest failure is likely to lead to discovering a missing command or data PDU ๐Ÿ•ต๏ธโ€โ™€๏ธ. In those cases, an implementer may want to lower the timeout values so recovery procedures start faster ๐Ÿƒโ€โ™€๏ธ๐Ÿ’จ.

9.4. Command Retry and Cleaning Old Command Instances

To keep old, retried command instances from showing up in a valid command window after a command sequence number wraps around, the protocol requires (see Section 3.2.2.1 Command Numbering and Acknowledging) that on every connection where a retry has been issued, a non-immediate command be issued and acknowledged within a 2**31-1 commands interval from the CmdSN of the retried command ๐Ÿ”. There are several ways an implementation can meet this requirement ๐Ÿงฉ.

The simplest technique is to send a (non-retry) non-immediate SCSI command (or a NOP if no SCSI command is available for a while) after every command retry on the connection where the retry was attempted ๐Ÿ€. Errors are deemed rare events, so this technique is probably the most effective, since it doesn't involve extra checks at the initiator when issuing commands ๐Ÿ†.

9.5. Synch and Steering Layer and Performance

A synch and steering layer is optional ๐Ÿ™‚. But an initiator or target that doesn't have it working against a target or initiator that demands synch and steering may see performance degradation caused by packet reordering and loss ๐Ÿ“‰. Providing a synch and steering mechanism is recommended for all high-speed implementations ๐Ÿš€โœจ.

9.6. Considerations for State-dependent Devices and Long-lasting SCSI Operations ๐ŸŽ€

Okay so, sequential access devices run on one main rule: where the device is depends on the last command it processed ๐Ÿ“ผ. That makes command processing order, plus knowing whether the previous command actually got processed, everything for data integrity ๐Ÿ˜ญ. Blindly retrying SCSI commands when you don't know if the last one went through is a real data integrity risk, bestie.

Picture this: a SCSI SPACE command to backspace one filemark gets issued, then gets re-issued because no status came back. If the first SPACE was actually processed, the re-issued SPACE (if it's processed too) moves the position again. So a later write lands in the wrong spot and whatever was there gets overwritten ๐Ÿ’€.

Now for medium changers ๐Ÿ”„. An EXCHANGE MEDIUM command where the SOURCE ADDRESS and DESTINATION ADDRESS are the same (so it's a swap) gets issued, then re-issued due to no status. If the first one was processed, the re-issued one performs the swap again. The net result is that no swap happened at all, which leaves a data integrity exposure ๐Ÿ™ˆ.

All commands that change the state of the device (SPACE commands for sequential access devices, and EXCHANGE MEDIUM for medium changer devices) MUST be issued as non-immediate commands for deterministic and in order delivery to iSCSI targets ๐Ÿซถ.

Many of those state-changing commands also assume they run exactly once โœจ. Devices that implement READ POSITION and LOCATE give you a way to do SCSI-level command recovery, and new tape-class devices should support those commands. Without them, a retry at SCSI level is difficult, so error recovery at iSCSI level is advisable ๐Ÿ‘‘.

Devices on long latency delivery subsystems that do long lasting SCSI operations may need mechanisms that enable connection replacement while commands are running (e.g., during an extended copy operation) ๐Ÿšš.

9.6.1. Determining the Proper ErrorRecoveryLevel ๐Ÿง 

The ErrorRecoveryLevel you use should be decided based on the deployment scenarios of a given iSCSI implementation. Generally, these factors must be considered before you pick the right level of recovery:

  • a) Application resilience to I/O failures.
  • b) Required level of availability in the face of transport connection failures.
  • c) Probability of transport layer "checksum escape". This in turn decides the iSCSI digest failure frequency, and thus the criticality of iSCSI-level error recovery. The details of estimating this probability are outside the scope of this document.

So if you look at those factors for SCSI tape devices as an example, implementations SHOULD use ErrorRecoveryLevel=1 when transport connection failure is not a concern and SCSI level recovery is unavailable ๐ŸŒธ. They SHOULD use ErrorRecoveryLevel=2 when the connection failure is also highly likely during a backup or retrieval ๐Ÿ’œ.

For extended copy operations, implementations SHOULD use ErrorRecoveryLevel=2 whenever there is a relatively high likelihood of connection failure ๐Ÿ”ฅ.


10. iSCSI PDU Formats ๐Ÿ“

All multi-byte integers that are specified in formats defined in this document are to be represented in network byte order (i.e., big endian) ๐ŸŒˆ. Any field that appears in this document assumes that the most significant byte is the lowest numbered byte and the most significant bit (within byte or field) is the lowest numbered bit unless specified otherwise.

Any compliant sender MUST set all bits not defined and all reserved fields to zero unless specified otherwise ๐Ÿงš. Any compliant receiver MUST ignore any bit not defined and all reserved fields unless specified otherwise. Receipt of reserved code values in defined fields MUST be reported as a protocol error โญ.

Reserved fields are marked by the word "reserved", some abbreviation of "reserved", or by "." for individual bits when no other form of marking is technically feasible ๐Ÿฆ‹.

10.1. iSCSI PDU Length and Padding

iSCSI PDUs are padded to the closest integer number of four byte words. The padding bytes SHOULD be sent as 0 ๐Ÿ’–.

10.2. PDU Template, Header, and Opcodes

All iSCSI PDUs have one or more header segments and, optionally, a data segment. After the entire header segment group a header-digest MAY follow. The data segment MAY also be followed by a data-digest ๐ŸŒธ.

The Basic Header Segment (BHS) is the first segment in all of the iSCSI PDUs. The BHS is a fixed-length 48-byte header segment. It MAY be followed by Additional Header Segments (AHS), a Header-Digest, a Data Segment, and/or a Data-Digest โœจ.

The overall structure of an iSCSI PDU is as follows ๐Ÿ‘‘:

Byte/     0       |       1       |       2       |       3       |
   /              |               |               |               |
  |0 1 2 3 4 5 6 7|0 1 2 3 4 5 6 7|0 1 2 3 4 5 6 7|0 1 2 3 4 5 6 7|
  +---------------+---------------+---------------+---------------+
 0/ Basic Header Segment (BHS)                                    /
 +/                                                               /
  +---------------+---------------+---------------+---------------+
48/ Additional Header Segment 1 (AHS)  (optional)                 /
 +/                                                               /
  +---------------+---------------+---------------+---------------+
  / Additional Header Segment 2 (AHS)  (optional)                 /
 +/                                                               /
  +---------------+---------------+---------------+---------------+
----
  +---------------+---------------+---------------+---------------+
  / Additional Header Segment n (AHS)  (optional)                 /
 +/                                                               /
  +---------------+---------------+---------------+---------------+
----
  +---------------+---------------+---------------+---------------+
 k/ Header-Digest (optional)                                      /
 +/                                                               /
  +---------------+---------------+---------------+---------------+
 l/ Data Segment(optional)                                        /
 +/                                                               /
  +---------------+---------------+---------------+---------------+
 m/ Data-Digest (optional)                                        /
 +/                                                               /
  +---------------+---------------+---------------+---------------+

All PDU segments and digests are padded to the closest integer number of four byte words. For example, all PDU segments and digests start at a four byte word boundary and the padding ranges from 0 to 3 bytes. The padding bytes SHOULD be sent as 0 ๐Ÿซง.

iSCSI response PDUs do not have AH Segments ๐Ÿ™…โ€โ™€๏ธ.

10.2.1. Basic Header Segment (BHS)

The BHS is 48 bytes long. The Opcode and DataSegmentLength fields appear in all iSCSI PDUs. In addition, when used, the Initiator Task Tag and Logical Unit Number always appear in the same location in the header ๐ŸŽ€.

The format of the BHS is ๐Ÿ’…:

Byte/     0       |       1       |       2       |       3       |
   /              |               |               |               |
  |0 1 2 3 4 5 6 7|0 1 2 3 4 5 6 7|0 1 2 3 4 5 6 7|0 1 2 3 4 5 6 7|
  +---------------+---------------+---------------+---------------+
 0|.|I| Opcode    |F|  Opcode-specific fields                     |
  +---------------+---------------+---------------+---------------+
 4|TotalAHSLength | DataSegmentLength                             |
  +---------------+---------------+---------------+---------------+
 8| LUN or Opcode-specific fields                                 |
  +                                                               +
12|                                                               |
  +---------------+---------------+---------------+---------------+
16| Initiator Task Tag                                            |
  +---------------+---------------+---------------+---------------+
20/ Opcode-specific fields                                        /
 +/                                                               /
  +---------------+---------------+---------------+---------------+
48

10.2.1.1. I

For request PDUs, the I bit set to 1 is an immediate delivery marker ๐Ÿš€.

10.2.1.2. Opcode

The Opcode indicates the type of iSCSI PDU the header encapsulates ๐Ÿง.

The Opcodes are divided into two categories: initiator opcodes and target opcodes. Initiator opcodes are in PDUs sent by the initiator (request PDUs). Target opcodes are in PDUs sent by the target (response PDUs) ๐Ÿ’œ.

Initiators MUST NOT use target opcodes and targets MUST NOT use initiator opcodes. That's a hard no, bestie ๐Ÿ™…โ€โ™€๏ธ.

Initiator opcodes defined in this specification are ๐ŸŒท:

0x00 NOP-Out
0x01 SCSI Command (encapsulates a SCSI Command Descriptor Block)
0x02 SCSI Task Management function request
0x03 Login Request
0x04 Text Request
0x05 SCSI Data-Out (for WRITE operations)
0x06 Logout Request
0x10 SNACK Request
0x1c-0x1e Vendor specific codes

Target opcodes are ๐ŸŒ™:

0x20 NOP-In
0x21 SCSI Response - contains SCSI status and possibly sense
 information or other response information.
0x22 SCSI Task Management function response
0x23 Login Response
0x24 Text Response
0x25 SCSI Data-In - for READ operations.
0x26 Logout Response
0x31 Ready To Transfer (R2T) - sent by target when it is ready
 to receive data.
0x32 Asynchronous Message - sent by target to indicate certain
 special conditions.
0x3c-0x3e Vendor specific codes
0x3f Reject

All other opcodes are reserved ๐Ÿ”’.

10.2.1.3. Final (F) bit

When set to 1, it indicates the final (or only) PDU of a sequence ๐Ÿ.

10.2.1.4. Opcode-specific Fields

These fields have different meanings for different opcode types ๐ŸŽญ.

10.2.1.5. TotalAHSLength

Total length of all AHS header segments in units of four byte words including padding, if any ๐Ÿ“.

The TotalAHSLength is only used in PDUs that have an AHS and MUST be 0 in all other PDUs. No exceptions, periodt ๐Ÿ’….

10.2.1.6. DataSegmentLength

This is the data segment payload length in bytes (excluding padding). The DataSegmentLength MUST be 0 whenever the PDU has no data segment ๐Ÿซถ.

10.2.1.7. LUN

Some opcodes operate on a specific Logical Unit. The Logical Unit Number (LUN) field identifies which Logical Unit ๐ŸŽฏ. If the opcode does not relate to a Logical Unit, this field is either ignored or may be used in an opcode specific way ๐ŸŒˆ.

The LUN field is 64-bits and should be formatted in accordance with [SAM2]. For example, LUN[0] from [SAM2] is BHS byte 8 and so on up to LUN[7] from [SAM2], which is BHS byte 15 ๐Ÿฆ„.

10.2.1.8. Initiator Task Tag โœจ

okay so the initiator hands out a Task Tag to every iSCSI task it sends ๐ŸŽ€ While a task exists, this tag MUST uniquely identify that task across the whole session ๐Ÿ’… SCSI can also use the initiator task tag as part of its own SCSI task identifier, but only when the time an iSCSI tag has to stay unique is at least as long as the time a SCSI task tag has to stay unique ๐Ÿงš And even untagged SCSI commands need an iSCSI Initiator Task Tag that exists and is unique, no exceptions ๐Ÿซถ

10.2.2. Additional Header Segment (AHS) ๐Ÿฆ‹

the general shape of an AHS looks like this ๐Ÿ‘€

Byte/     0       |       1       |       2       |       3       |
   /              |               |               |               |
  |0 1 2 3 4 5 6 7|0 1 2 3 4 5 6 7|0 1 2 3 4 5 6 7|0 1 2 3 4 5 6 7|
  +---------------+---------------+---------------+---------------+
 0| AHSLength                     | AHSType       | AHS-Specific  |
  +---------------+---------------+---------------+---------------+
 4/ AHS-Specific                                                  /
 +/                                                               /
  +---------------+---------------+---------------+---------------+
 x

10.2.2.1. AHSType ๐Ÿ’œ

the AHSType field is coded like this ๐Ÿ“

    bit 0-1 - Reserved

    bit 2-7 - AHS code

     0 - Reserved
     1 - Extended CDB
     2 - Expected Bidirectional Read Data Length
     3 - 63 Reserved

10.2.2.2. AHSLength ๐ŸŒธ

this field holds the effective length in bytes of the AHS, not counting AHSType, AHSLength, or any padding ๐Ÿ™ˆ the AHS gets padded out to the smallest whole number of 4 byte words, so that's anywhere from 0 up to 3 padding bytes, bestie ๐Ÿ’–

10.2.2.3. Extended CDB AHS ๐ŸŒˆ

the format of the Extended CDB AHS is ๐Ÿ”ฅ

Byte/     0       |       1       |       2       |       3       |
   /              |               |               |               |
  |0 1 2 3 4 5 6 7|0 1 2 3 4 5 6 7|0 1 2 3 4 5 6 7|0 1 2 3 4 5 6 7|
  +---------------+---------------+---------------+---------------+
 0| AHSLength (CDBLength-15)      | 0x01          | Reserved      |
  +---------------+---------------+---------------+---------------+
 4/ ExtendedCDB...+padding                                        /
 +/                                                               /
  +---------------+---------------+---------------+---------------+
 x
this type of AHS MUST NOT be used if the CDBLength is less than 17 ๐Ÿšจ the length includes the reserved byte 3, so keep that in mind ๐Ÿซ 

10.2.2.4. Bidirectional Expected Read-Data Length AHS โœจ

the format of the Bidirectional Read Expected Data Transfer Length AHS is ๐Ÿ’…

Byte/     0       |       1       |       2       |       3       |
   /              |               |               |               |
  |0 1 2 3 4 5 6 7|0 1 2 3 4 5 6 7|0 1 2 3 4 5 6 7|0 1 2 3 4 5 6 7|
  +---------------+---------------+---------------+---------------+
 0| AHSLength (0x0005)            | 0x02          | Reserved      |
  +---------------+---------------+---------------+---------------+
 4| Expected Read-Data Length                                     |
  +---------------+---------------+---------------+---------------+
 8

10.2.3. Header Digest and Data Digest ๐Ÿ›ก๏ธ

okay so optional header and data digests protect the integrity of the header and the data, one each ๐Ÿซถ the digests, if they're there, sit right after the header and the PDU-specific data, and they cover the header and the PDU data respectively, padding bytes included if there are any ๐Ÿฆ‹

the existence and type of digests get negotiated during the Login Phase ๐Ÿค

splitting the header and data digests is super useful in iSCSI routing, because only the header changes when a message gets forwarded ๐ŸŒท so in that case only the header digest should be recalculated ๐Ÿงš

digests are not counted in the data or header length fields ๐Ÿ™…โ€โ™€๏ธ

and a zero-length Data Segment also implies a zero-length data-digest ๐Ÿ’€

10.2.4. Data Segment ๐ŸŽ€

the (optional) Data Segment carries PDU associated data ๐Ÿ“ its payload effective length lives in the BHS field called DataSegmentLength ๐Ÿ”ฅ and the Data Segment gets padded to a whole number of 4 byte words too ๐Ÿ’–

10.3. SCSI Command ๐Ÿš€

the format of the SCSI Command PDU is ๐Ÿ‘‘

Byte/     0       |       1       |       2       |       3       |
   /              |               |               |               |
  |0 1 2 3 4 5 6 7|0 1 2 3 4 5 6 7|0 1 2 3 4 5 6 7|0 1 2 3 4 5 6 7|
  +---------------+---------------+---------------+---------------+
 0|.|I| 0x01      |F|R|W|. .|ATTR | Reserved                      |
  +---------------+---------------+---------------+---------------+
 4|TotalAHSLength | DataSegmentLength                             |
  +---------------+---------------+---------------+---------------+
 8| Logical Unit Number (LUN)                                     |
  +                                                               +
12|                                                               |
  +---------------+---------------+---------------+---------------+
16| Initiator Task Tag                                            |
  +---------------+---------------+---------------+---------------+
20| Expected Data Transfer Length                                 |
  +---------------+---------------+---------------+---------------+
24| CmdSN                                                         |
  +---------------+---------------+---------------+---------------+
28| ExpStatSN                                                     |
  +---------------+---------------+---------------+---------------+
32/ SCSI Command Descriptor Block (CDB)                           /
 +/                                                               /
  +---------------+---------------+---------------+---------------+
48/ AHS (Optional)                                                /
  +---------------+---------------+---------------+---------------+
 x/ Header Digest (Optional)                                      /
  +---------------+---------------+---------------+---------------+
 y/ (DataSegment, Command Data) (Optional)                        /
 +/                                                               /
  +---------------+---------------+---------------+---------------+
 z/ Data Digest (Optional)                                        /
  +---------------+---------------+---------------+---------------+

10.3.1. Flags and Task Attributes (byte 1) ๐Ÿšฉ

the flags for a SCSI Command are ๐ŸŒˆ

bit 0   (F) is set to 1 when no unsolicited SCSI Data-Out PDUs follow
         this PDU.  When F=1 for a write and if Expected Data
         Transfer Length is larger than the DataSegmentLength, the
         target may solicit additional data through R2T.

bit 1   (R) is set to 1 when the command is expected to input data.

bit 2   (W) is set to 1 when the command is expected to output data.

bit 3-4 Reserved.

bit 5-7 contains Task Attributes.

Task Attributes (ATTR) have one of these integer values (see [SAM2] for details) โœจ

  0 - Untagged
  1 - Simple
  2 - Ordered
  3 - Head of Queue
  4 - ACA
  5-7 - Reserved

setting both the W and the F bit to 0 is an error, bestie ๐Ÿ™…โ€โ™€๏ธ either or both of R and W MAY be 1 when the Expected Data Transfer Length and/or Bidirectional Read Expected Data Transfer Length are 0 ๐Ÿ’… but they MUST NOT both be 0 when those lengths are not 0 ๐Ÿซถ (so when some data transfer is expected, the transfer direction is shown by the R and/or W bit) ๐Ÿฆ‹

10.3.2. CmdSN - Command Sequence Number ๐ŸŽ€

this one enables ordered delivery across multiple connections in a single session ๐Ÿงš

10.3.3. ExpStatSN ๐ŸŒธ

command responses up to ExpStatSN-1 (mod 2**32) have been received on the connection, which is how it acknowledges status ๐Ÿ’–

10.3.4. Expected Data Transfer Length ๐Ÿ“

for unidirectional operations, this field holds the number of bytes of data involved in the SCSI operation ๐Ÿ”ฅ for a unidirectional write (W flag set to 1 and R flag set to 0), the initiator uses this field to say how many bytes it expects to send for the operation ๐Ÿ‘€ for a unidirectional read (W flag set to 0 and R flag set to 1), the initiator uses it to say how many bytes it expects the target to send back ๐ŸŒˆ it corresponds to the SAM2 byte count ๐Ÿ’…

for bidirectional operations (both R and W flags set to 1), this field holds the number of data bytes in the write transfer ๐Ÿซถ for bidirectional operations, an additional header segment MUST be present in the header sequence that indicates the Bidirectional Read Expected Data Transfer Length โœจ the Expected Data Transfer Length field and the Bidirectional Read Expected Data Transfer Length field both correspond to the SAM2 byte count ๐Ÿ’œ

if the Expected Data Transfer Length for a write and the length of the immediate data part that follows the command (if there is any) are the same, then no more data PDUs are expected to follow ๐ŸŽ€ in that case, the F bit MUST be set to 1 ๐Ÿšจ

if the Expected Data Transfer Length is higher than the FirstBurstLength (the negotiated maximum amount of unsolicited data the target will accept), the initiator MUST send the maximum amount of unsolicited data OR ONLY the immediate data, if any ๐Ÿ™ˆ

when a data transfer finishes, the target tells the initiator (through residual counts) how many bytes it actually processed, meaning sent and/or received ๐Ÿงš

10.3.5. CDB - SCSI Command Descriptor Block ๐ŸŒท

the CDB field has 16 bytes, which is enough for the commonly used CDBs ๐Ÿ’– whenever the CDB is larger than 16 bytes, an Extended CDB AHS MUST be used to hold the CDB spillover ๐Ÿ‘‘

10.3.6. Data Segment - Command Data ๐Ÿฆ‹

some SCSI commands need extra parameter data along with the command ๐Ÿ“ that data can go past the iSCSI header boundary in a data segment ๐ŸŒธ or, alternatively, user data (like from a WRITE operation) can go in the data segment, and both cases are called immediate data ๐Ÿ’… these data follow the rules for solicited vs. unsolicited data outlined in Section 3.2.4.2 Data Transfer Overview ๐Ÿ”ฅ

10.4. SCSI Response ๐ŸŽ€

Okay so the SCSI Response PDU is the message the target sends back to say "I'm done with your command, here's how it went" ๐Ÿ’… Bestie, the layout is giving very specific vibes, so here's the whole thing exactly as it goes:

   Byte/     0       |       1       |       2       |       3       |
      /              |               |               |               |
     |0 1 2 3 4 5 6 7|0 1 2 3 4 5 6 7|0 1 2 3 4 5 6 7|0 1 2 3 4 5 6 7|
     +---------------+---------------+---------------+---------------+
    0|.|.| 0x21      |1|. .|o|u|O|U|.| Response      | Status        |
     +---------------+---------------+---------------+---------------+
    4|TotalAHSLength | DataSegmentLength                             |
     +---------------+---------------+---------------+---------------+
    8| Reserved                                                      |
     +                                                               +
   12|                                                               |
     +---------------+---------------+---------------+---------------+
   16| Initiator Task Tag                                            |
     +---------------+---------------+---------------+---------------+
   20| SNACK Tag or Reserved                                         |
     +---------------+---------------+---------------+---------------+
   24| StatSN                                                        |
     +---------------+---------------+---------------+---------------+
   28| ExpCmdSN                                                      |
     +---------------+---------------+---------------+---------------+
   32| MaxCmdSN                                                      |
     +---------------+---------------+---------------+---------------+
   36| ExpDataSN or Reserved                                         |
     +---------------+---------------+---------------+---------------+
   40| Bidirectional Read Residual Count or Reserved                 |
     +---------------+---------------+---------------+---------------+
   44| Residual Count or Reserved                                    |
     +---------------+---------------+---------------+---------------+
   48| Header-Digest (Optional)                                      |
     +---------------+---------------+---------------+---------------+
     / Data Segment (Optional)                                       /
    +/                                                               /
     +---------------+---------------+---------------+---------------+
     | Data-Digest (Optional)                                        |
     +---------------+---------------+---------------+---------------+

10.4.1. Flags (byte 1) ๐Ÿฆ‹

Bestie, the flag bits are where the drama is โœจ

     bit 1-2 Reserved.

bit 3 is (o), and it gets set for Bidirectional Read Residual Overflow ๐Ÿ™ˆ Then the Bidirectional Read Residual Count tells you how many bytes never made it back to the initiator, because the initiator's Expected Bidirectional Read Data Transfer Length wasn't big enough.

bit 4 is (u), for Bidirectional Read Residual Underflow ๐Ÿ’œ Here the Bidirectional Read Residual Count tells you how many bytes didn't get transferred to the initiator, out of the number that were expected to be transferred.

bit 5 is (O), for Residual Overflow ๐ŸŒธ The Residual Count shows how many bytes weren't transferred because the initiator's Expected Data Transfer Length wasn't sufficient. For a bidirectional operation, the Residual Count holds the residual for the write operation.

bit 6 is (U), for Residual Underflow ๐Ÿ“ The Residual Count shows how many bytes weren't transferred out of the number that were expected to be transferred. For a bidirectional operation, the Residual Count holds the residual for the write operation.

bit 7 is (0), and it's Reserved ๐Ÿ’€

Bits O and U, and bits o and u, are mutually exclusive, so having both o and u set, or both O and U set to 1, is a protocol error ๐Ÿšจ Also, for a response other than "Command Completed at Target", bits 3-6 MUST be 0.

10.4.2. Status ๐Ÿ’–

The Status field reports the SCSI status of the command (as specified in [SAM2]), and it's only valid if the Response Code is Command Completed at target ๐Ÿงš

Some of the status codes defined in [SAM2] are:

     0x00 GOOD
     0x02 CHECK CONDITION
     0x08 BUSY
     0x18 RESERVATION CONFLICT
     0x28 TASK SET FULL
     0x30 ACA ACTIVE
     0x40 TASK ABORTED

See [SAM2] for the complete list and definitions ๐Ÿ“š

Okay, this part is kinda intense ๐Ÿ˜ญ If a SCSI device error gets detected while data from the initiator is still expected (the command PDU didn't contain all the data and the target hasn't received a Data PDU with the final bit Set), the target MUST wait until it receives a Data PDU with the F bit set in the last expected sequence before it sends the Response PDU. No skipping ahead, bestie ๐Ÿ™…โ€โ™€๏ธ

10.4.3. Response ๐ŸŒˆ

This field contains the iSCSI service response ๐Ÿ‘‘

iSCSI service response codes defined in this specification are:

     0x00 - Command Completed at Target
     0x01 - Target Failure
     0x80-0xff - Vendor specific

All other response codes are reserved ๐Ÿ”’

The Response is used to report a Service Response. Mapping the response code into a SCSI service response code value, if needed, is outside the scope of this document. But symbolically, response value 0x00 maps to the SCSI service response (see [SAM2] and [SPC3]) of TASK COMPLETE or LINKED COMMAND COMPLETE. Every other Response value maps to the SCSI service response of SERVICE DELIVERY OR TARGET FAILURE ๐Ÿซถ

If a PDU that includes SCSI status (a Response PDU or a Data-In PDU including status) doesn't arrive before the session is terminated, the SCSI service response is SERVICE DELIVERY OR TARGET FAILURE ๐Ÿ˜ญ

A non-zero Response field indicates a failure to execute the command, and in that case the Status and Flag fields are undefined ๐Ÿ’€

10.4.4. SNACK Tag ๐ŸŽ€

This field contains a copy of the SNACK Tag of the last SNACK Tag accepted by the target on the same connection and for the command the response is issued for. Otherwise it's reserved and should be set to 0 โœจ

After issuing a R-Data SNACK, the initiator must discard any SCSI status unless it's contained in an SCSI Response PDU carrying the same SNACK Tag as the last issued R-Data SNACK for the SCSI command on the current connection ๐Ÿ™…โ€โ™€๏ธ

For a detailed discussion on R-Data SNACK, see Section 10.16 SNACK Request ๐Ÿฆ‹

10.4.5. Residual Count ๐Ÿ’…

The Residual Count field MUST be valid in the case where either the U bit or the O bit is set. If neither bit is set, the Residual Count field is reserved. Targets may set the residual count, and initiators may use it when the response code is "completed at target" (even if the status returned isn't GOOD). If the O bit is set, the Residual Count indicates the number of bytes that weren't transferred because the initiator's Expected Data Transfer Length wasn't sufficient. If the U bit is set, the Residual Count indicates the number of bytes that weren't transferred out of the number of bytes expected to be transferred ๐ŸŒธ

10.4.6. Bidirectional Read Residual Count ๐Ÿ“

The Bidirectional Read Residual Count field MUST be valid in the case where either the u bit or the o bit is set. If neither bit is set, the Bidirectional Read Residual Count field is reserved. Targets may set the Bidirectional Read Residual Count, and initiators may use it when the response code is "completed at target". If the o bit is set, the Bidirectional Read Residual Count indicates the number of bytes that weren't transferred to the initiator because the initiator's Expected Bidirectional Read Transfer Length wasn't sufficient. If the u bit is set, the Bidirectional Read Residual Count indicates the number of bytes that weren't transferred to the initiator out of the number of bytes expected to be transferred ๐Ÿฆ‹

10.4.7. Data Segment - Sense and Response Data Segment ๐Ÿ”ฅ

iSCSI targets MUST support and enable autosense. If Status is CHECK CONDITION (0x02), then the Data Segment MUST contain sense data for the failed command ๐Ÿšจ

For some iSCSI responses, the response data segment MAY contain some response related information (e.g., for a target failure, it may contain a vendor specific detailed description of the failure) ๐Ÿงš

If the DataSegmentLength is not 0, the format of the Data Segment is as follows:

   Byte/     0       |       1       |       2       |       3       |
      /              |               |               |               |
     |0 1 2 3 4 5 6 7|0 1 2 3 4 5 6 7|0 1 2 3 4 5 6 7|0 1 2 3 4 5 6 7|
     +---------------+---------------+---------------+---------------+
    0|SenseLength                    | Sense Data                    |
     +---------------+---------------+---------------+---------------+
    x/ Sense Data                                                    /
     +---------------+---------------+---------------+---------------+
    y/ Response Data                                                 /
     /                                                               /
     +---------------+---------------+---------------+---------------+
    z|

10.4.7.1. SenseLength ๐ŸŒท

Length of Sense Data ๐ŸŒท

10.4.7.2. Sense Data ๐ŸŒˆ

The Sense Data contains detailed information about a check condition, and [SPC3] specifies the format and content of the Sense Data ๐Ÿ‘‘

Certain iSCSI conditions result in the command being terminated at the target (response Command Completed at Target) with a SCSI Check Condition Status, as outlined in the next table ๐Ÿ’–

   +--------------------------+----------+---------------------------+
   | iSCSI Condition          |Sense     | Additional Sense Code &   |
   |                          |Key       | Qualifier                 |
   +--------------------------+----------+---------------------------+
   | Unexpected unsolicited   |Aborted   | ASC = 0x0c ASCQ = 0x0c    |
   | data                     |Command-0B| Write Error               |
   +--------------------------+----------+---------------------------+
   | Incorrect amount of data |Aborted   | ASC = 0x0c ASCQ = 0x0d    |
   |                          |Command-0B| Write Error               |
   +--------------------------+----------+---------------------------+
   | Protocol Service CRC     |Aborted   | ASC = 0x47 ASCQ = 0x05    |
   | error                    |Command-0B| CRC Error Detected        |
   +--------------------------+----------+---------------------------+
   | SNACK rejected           |Aborted   | ASC = 0x11 ASCQ = 0x13    |
   |                          |Command-0B| Read Error                |
   +--------------------------+----------+---------------------------+

The target reports the "Incorrect amount of data" condition if, during data output, the total data length to output is greater than FirstBurstLength and the initiator sent unsolicited non-immediate data, but the total amount of unsolicited data is different than FirstBurstLength. The target reports the same error when the amount of data sent as a reply to an R2T doesn't match the amount requested ๐Ÿ™ˆ

10.4.8. ExpDataSN ๐Ÿงš

The number of R2T and Data-In (read) PDUs the target has sent for the command ๐Ÿซถ

This field MUST be 0 if the response code is not Command Completed at Target, or the target sent no Data-In PDUs for the command ๐Ÿ”’

10.4.9. StatSN - Status Sequence Number ๐Ÿ’–

okay so StatSN is a Sequence Number that the target iSCSI layer generates per connection โœจ, and its whole job is to let the initiator acknowledge that it got the status ๐Ÿซถ. It goes up by 1 for every response/status sent on a connection, EXCEPT for responses sent because of a retry or SNACK, since those are kind of a special case ๐Ÿ™ˆ. And if a response is being resent because of a retransmission request, the StatSN MUST be the same as the first time that PDU was sent, unless the connection has since been restarted. no cap, that's the rule, bestie ๐Ÿ’…

10.4.10. ExpCmdSN - Next Expected CmdSN from this Initiator ๐ŸŽ€

ExpCmdSN is a Sequence Number that the target iSCSI sends back to the initiator to say "yup, got your commands" ๐Ÿฆ‹. The initiator uses it to update a local variable with the same name. Also, if ExpCmdSN is equal to MaxCmdSN+1, that's the target basically saying "I can't take any new commands rn" ๐Ÿ’€

10.4.11. MaxCmdSN - Maximum CmdSN from this Initiator ๐ŸŒธ

MaxCmdSN is another Sequence Number the target sends back, and this one tells the initiator the maximum CmdSN it's allowed to send ๐Ÿ“. Same deal as before, the initiator updates a local variable with the same name. If MaxCmdSN is equal to ExpCmdSN-1, that means the target can't receive any more commands, so it's a whole vibe check ๐Ÿ™ˆ. Now, when MaxCmdSN changes at the target while the target has no pending PDUs to pass this info along, the target MUST generate a NOP-IN to carry the new MaxCmdSN. that one is non-negotiable, periodt โœจ


10.5. Task Management Function Request ๐Ÿ‘‘

Here's the layout for this PDU, bestie ๐Ÿงš

   Byte/     0       |       1       |       2       |       3       |
      /              |               |               |               |
     |0 1 2 3 4 5 6 7|0 1 2 3 4 5 6 7|0 1 2 3 4 5 6 7|0 1 2 3 4 5 6 7|
     +---------------+---------------+---------------+---------------+
    0|.|I| 0x02      |1| Function    | Reserved                      |
     +---------------+---------------+---------------+---------------+
    4|TotalAHSLength | DataSegmentLength                             |
     +---------------+---------------+---------------+---------------+
    8| Logical Unit Number (LUN) or Reserved                         |
     +                                                               +
   12|                                                               |
     +---------------+---------------+---------------+---------------+
   16| Initiator Task Tag                                            |
     +---------------+---------------+---------------+---------------+
   20| Referenced Task Tag or 0xffffffff                             |
     +---------------+---------------+---------------+---------------+
   24| CmdSN                                                         |
     +---------------+---------------+---------------+---------------+
   28| ExpStatSN                                                     |
     +---------------+---------------+---------------+---------------+
   32| RefCmdSN or Reserved                                          |
     +---------------+---------------+---------------+---------------+
   36| ExpDataSN or Reserved                                         |
     +---------------+---------------+---------------+---------------+
   40/ Reserved                                                      /
    +/                                                               /
     +---------------+---------------+---------------+---------------+
   48| Header-Digest (Optional)                                      |
     +---------------+---------------+---------------+---------------+

10.5.1. Function ๐Ÿฆ‹

Task Management is how an initiator gets to explicitly control one or more Tasks (SCSI and iSCSI tasks) โœจ. These are the function codes, lowkey the menu of drama ๐Ÿ’… (for a deeper dive on SCSI task management, check out [SAM2]):

  • 1 - ABORT TASK - aborts the task identified by the Referenced Task Tag field.
  • 2 - ABORT TASK SET - aborts all Tasks issued via this session on the logical unit.
  • 3 - CLEAR ACA - clears the Auto Contingent Allegiance condition.
  • 4 - CLEAR TASK SET - aborts all Tasks in the appropriate task set as defined by the TST field in the Control mode page (see [SPC3]).
  • 5 - LOGICAL UNIT RESET
  • 6 - TARGET WARM RESET
  • 7 - TARGET COLD RESET
  • 8 - TASK REASSIGN - reassigns connection allegiance for the task identified by the Referenced Task Tag field to this connection, thus resuming the iSCSI exchanges for the task.

For all of these, the Task Management function response MUST be returned as detailed in Section 10.6 Task Management Function Response ๐ŸŽ€. All of these functions apply to the referenced tasks, whether they're proper SCSI tasks or tagged iSCSI operations, so no favourites ๐Ÿซถ. Task management requests must act on all the commands from the same session that have a CmdSN lower than the task management CmdSN. LOGICAL UNIT RESET, TARGET WARM RESET and TARGET COLD RESET may affect commands from other sessions, or commands from the same session with CmdSN equal or exceeding CmdSN ๐Ÿ‘€.

If the task management request is marked for immediate delivery, it must be considered immediately for execution, but the operations involved (all or part of them) may be postponed so the target can receive all the relevant tasks โณ. According to [SAM2], for all the tasks covered by the Task Management response (so, the ones with CmdSN lower than the task management command CmdSN), except the Task Management response to a TASK REASSIGN, additional responses MUST NOT be delivered to the SCSI layer after the Task Management response ๐Ÿšซ. The iSCSI initiator MAY deliver to the SCSI layer all responses it received before the Task Management response. It's an implementation choice whether SCSI responses received before the Task Management response, but after the task management request was issued, get passed up to the SCSI layer by the iSCSI layer in the initiator ๐Ÿงš. The iSCSI target MUST ensure that no responses for the tasks covered by a task management function are delivered to the iSCSI initiator after the Task Management response, except for a task covered by a TASK REASSIGN ๐Ÿ”ฅ.

For ABORT TASK SET and CLEAR TASK SET, the issuing initiator MUST continue to respond to all valid target transfer tags (received via R2T, Text Response, NOP-In, or SCSI Data-In PDUs) related to the affected task set, even after issuing the task management request ๐Ÿ’…. The issuing initiator SHOULD however terminate these response sequences (by setting the F-bit to 1) as quickly as possible โšก. The target on its part MUST wait for responses on all affected target transfer tags before acting on either of these two task management requests ๐Ÿซถ. If all or part of the response sequence is not received for a valid TTT (because of digest errors), the target MAY treat it as a case of within-command error recovery class (see Section 6.1.4.1 Recovery Within-command) if it supports ErrorRecoveryLevel >= 1. Or it may drop the connection to complete the requested task set function ๐Ÿ™ˆ.

If an ABORT TASK is issued for a task created by an immediate command, then RefCmdSN MUST be that of the Task Management request itself (so CmdSN and RefCmdSN are equal). Otherwise, RefCmdSN MUST be set to the CmdSN of the task to be aborted, which is lower than CmdSN โœจ.

If the connection is still active (it's not undergoing an implicit or explicit logout), ABORT TASK MUST be issued on the same connection that the task to be aborted is allegiant to at the time the Task Management Request is issued ๐ŸŽ€. If the connection is implicitly or explicitly logged out (so no other request will be issued on the failing connection, and no other response will be received on it), then an ABORT TASK function request may be issued on another connection. This Task Management request will then establish a new allegiance for the command to be aborted AND abort it. So the task won't have to be retried or reassigned, and its status, if it was issued but not acknowledged, will be reissued followed by the Task Management response ๐Ÿฆ‹.

At the target, an ABORT TASK function MUST NOT be executed on a Task Management request. Such a request MUST result in a Task Management response of "Function rejected" ๐Ÿšซ

For the LOGICAL UNIT RESET function, the target MUST behave as dictated by the Logical Unit Reset function in [SAM2] ๐Ÿ’–.

The TARGET WARM RESET and TARGET COLD RESET functions are OPTIONAL to implement. When they are implemented, they should act as described below ๐ŸŒˆ. The TARGET WARM RESET is also subject to SCSI access controls on the requesting initiator, as defined in [SPC3]. When authorization fails at the target, the target MUST return the appropriate response described in Section 10.6 Task Management Function Response ๐Ÿ”. The TARGET COLD RESET function is not subject to SCSI access controls, but its execution privileges may be managed by iSCSI mechanisms such as login authentication ๐Ÿงš.

When executing the TARGET WARM RESET and TARGET COLD RESET functions, the target cancels all pending operations on all Logical Units known by the issuing initiator. Both functions are equivalent to the Target Reset function specified by [SAM2]. They can affect many other initiators logged in with the servicing SCSI target port ๐ŸŒธ.

The target MUST treat the TARGET COLD RESET function additionally as a power on event, which means it terminates all of its TCP connections to all initiators (all sessions are terminated) ๐Ÿ’€. For this reason, the Service Response (defined by [SAM2]) for this SCSI task management function may not be reliably delivered to the issuing initiator port ๐Ÿ˜ญ.

For the TASK REASSIGN function, the target should reassign the connection allegiance to this new connection, and thus resume iSCSI exchanges for the task ๐Ÿ”. TASK REASSIGN MUST ONLY be received by the target after the connection on which the command was previously executing has been successfully logged out. The Task Management response MUST be issued before the reassignment becomes effective. For more on how this works, see Section 6.2 Retry and Reassign in Recovery ๐Ÿ“.

At the target, a TASK REASSIGN function request MUST NOT be executed to reassign the connection allegiance of a Task Management function request, an active text negotiation task, or a Logout task. Such a request MUST result in a Task Management response of "Function rejected" ๐Ÿ™…โ€โ™€๏ธ

TASK REASSIGN MUST be issued as an immediate command โญ.

10.5.2. TotalAHSLength and DataSegmentLength ๐Ÿ“

For this PDU, TotalAHSLength and DataSegmentLength MUST be 0 ๐Ÿซถ.

10.5.3. LUN ๐ŸŒธ

This field is required for functions that address a specific LU (ABORT TASK, CLEAR TASK SET, ABORT TASK SET, CLEAR ACA, LOGICAL UNIT RESET) and is reserved in all the others ๐ŸŽ€.

10.5.4. Referenced Task Tag ๐Ÿฆ‹

This is the Initiator Task Tag of the task to be aborted for the ABORT TASK function, or the one to be reassigned for the TASK REASSIGN function. For all the other functions, this field MUST be set to the reserved value 0xffffffff ๐Ÿ’œ.

10.5.5. RefCmdSN ๐Ÿงš

If an ABORT TASK is issued for a task created by an immediate command, then RefCmdSN MUST be that of the Task Management request itself (so CmdSN and RefCmdSN are equal) โœจ.

For an ABORT TASK of a task created by a non-immediate command, RefCmdSN MUST be set to the CmdSN of the task identified by the Referenced Task Tag field. Targets must use this field as described in section 10.6.1 when the task identified by the Referenced Task Tag field is not with the target ๐Ÿ‘€.

Otherwise, this field is reserved ๐Ÿ’….

10.5.6. ExpDataSN โœจ

okay bestie, so the target and the initiator both keep track of a data acknowledgement reference number ๐ŸŽ€. It's the first input DataSN the initiator hasn't acknowledged yet ๐Ÿฆ‹. When a new command gets issued, this number is set to 0 ๐Ÿ’–. If the function is TASK REASSIGN (the one that sets up a new connection allegiance for a Read or Bidirectional command that was already issued) ๐Ÿ”, then ExpDataSN holds either an updated data acknowledgement reference number or the value 0. Zero just means the reference number hasn't changed ๐Ÿ’….

The initiator MUST discard any data PDUs from the previous execution that it didn't acknowledge, and the target MUST transmit all Data-In PDUs (if any) starting from the data acknowledgement reference number ๐Ÿซถ. How many PDUs get resent might or might not match the original, depending on whether MaxRecvDataSegmentLength changed during the reassignment ๐Ÿคทโ€โ™€๏ธ. The target MAY also send no more Data-In PDUs if all the data has already been acknowledged โœจ.

The value of ExpDataSN MUST be 0 or higher than the DataSN of the last acknowledged Data-In PDU, but not larger than DataSN+1 of the last Data-In PDU sent by the target. Any other value MUST be ignored by the target ๐Ÿ™ˆ. Main character energy for the target: it ignores anything sus ๐Ÿ’….

For other functions this field is reserved ๐ŸŒธ.


10.6. Task Management Function Response ๐Ÿ‘‘

Here's the layout, bestie ๐Ÿงš:

Byte/     0       |       1       |       2       |       3       |
   /              |               |               |               |
  |0 1 2 3 4 5 6 7|0 1 2 3 4 5 6 7|0 1 2 3 4 5 6 7|0 1 2 3 4 5 6 7|
  +---------------+---------------+---------------+---------------+
 0|.|.| 0x22      |1| Reserved    | Response      | Reserved      |
  +---------------+---------------+---------------+---------------+
 4|TotalAHSLength | DataSegmentLength                             |
  +---------------------------------------------------------------+
 8/ Reserved                                                      /
  /                                                               /
  +---------------+---------------+---------------+---------------+
16| Initiator Task Tag                                            |
  +---------------+---------------+---------------+---------------+
20| Reserved                                                      |
  +---------------+---------------+---------------+---------------+
24| StatSN                                                        |
  +---------------+---------------+---------------+---------------+
28| ExpCmdSN                                                      |
  +---------------+---------------+---------------+---------------+
32| MaxCmdSN                                                      |
  +---------------+---------------+---------------+---------------+
36/ Reserved                                                      /
 +/                                                               /
  +---------------+---------------+---------------+---------------+
48| Header-Digest (Optional)                                      |
  +---------------+---------------+---------------+---------------+

For the functions ABORT TASK, ABORT TASK SET, CLEAR ACA, CLEAR TASK SET, LOGICAL UNIT RESET, TARGET COLD RESET, TARGET WARM RESET and TASK REASSIGN, the target performs the requested Task Management function and sends a Task Management response back to the initiator. For TASK REASSIGN, the new connection allegiance MUST ONLY become effective at the target after the target issues the Task Management Response ๐Ÿฆ‹.


10.6.1. Response ๐Ÿ’–

The target provides a Response, which may take on the following values:

  • a) 0 - Function complete.
  • b) 1 - Task does not exist.
  • c) 2 - LUN does not exist.
  • d) 3 - Task still allegiant.
  • e) 4 - Task allegiance reassignment not supported.
  • f) 5 - Task management function not supported.
  • g) 6 - Function authorization failed.
  • h) 255 - Function rejected.

All other values are reserved ๐ŸŒˆ.

For a discussion on usage of response codes 3 and 4, see Section 6.2.2 Allegiance Reassignment ๐Ÿ“–.

For the TARGET COLD RESET and TARGET WARM RESET functions, the target cancels all pending operations across all Logical Units known to the issuing initiator. For the TARGET COLD RESET function, the target MUST then close all of its TCP connections to all initiators (terminates all sessions) ๐Ÿ’€.

The mapping of the response code into a SCSI service response code value, if needed, is outside the scope of this document. However, in symbolic terms, Response values 0 and 1 map to the SCSI service response of FUNCTION COMPLETE. All other Response values map to the SCSI service response of FUNCTION REJECTED. If a Task Management function response PDU does not arrive before the session is terminated, the SCSI service response is SERVICE DELIVERY OR TARGET FAILURE ๐Ÿ˜ญ.

The response to ABORT TASK SET and CLEAR TASK SET MUST only be issued by the target after all of the commands affected have been received by the target, the corresponding task management functions have been executed by the SCSI target, and the delivery of all responses delivered until the task management function completion have been confirmed (acknowledged through ExpStatSN) by the initiator on all connections of this session. For the exact timeline of events, refer to Section 10.6.2 Task Management Actions on Task Sets ๐Ÿ—“๏ธ.

For the ABORT TASK function:

  • a) If the Referenced Task Tag identifies a valid task leading to a successful termination, then targets must return the "Function complete" response. โœ…
  • b) If the Referenced Task Tag does not identify an existing task, but if the CmdSN indicated by the RefCmdSN field in the Task Management function request is within the valid CmdSN window and less than the CmdSN of the Task Management function request itself, then targets must consider the CmdSN received and return the "Function complete" response. ๐Ÿซถ
  • c) If the Referenced Task Tag does not identify an existing task and if the CmdSN indicated by the RefCmdSN field in the Task Management function request is outside the valid CmdSN window, then targets must return the "Task does not exist" response. ๐Ÿ™…โ€โ™€๏ธ

10.6.2. Task Management Actions on Task Sets ๐ŸŽ€

The execution of ABORT TASK SET and CLEAR TASK SET Task Management function requests consists of the following sequence of events, in the specified order, on each of the entities. Think of it like a choreographed group routine ๐Ÿ’ƒ.

The initiator:

  • a) Issues ABORT TASK SET/CLEAR TASK SET request.
  • b) Continues to respond to each target transfer tag received for the affected task set.
  • c) Receives any responses for the tasks in the affected task set (may process them as usual because they are guaranteed to be valid).
  • d) Receives the task set management response, thus concluding all the tasks in the affected task set. ๐ŸŽ‰

The target:

  • a) Receives the ABORT TASK SET/CLEAR TASK SET request.
  • b) Waits for all target transfer tags to be responded to and for all affected tasks in the task set to be received.
  • c) Propagates the command to, and receives the response from, the target SCSI layer.
  • d) Takes note of last-sent StatSN on each of the connections in the iSCSI sessions (one or more) sharing the affected task set, and waits for acknowledgement of each StatSN (may solicit for acknowledgement by way of a NOP-In). If some tasks originate from non-iSCSI I_T_L nexi, then the means by which the target insures that all affected tasks have returned their status to the initiator are defined by the specific protocol. ๐Ÿง
  • e) Sends the task set management response to the issuing initiator. ๐Ÿ’œ

10.6.3. TotalAHSLength and DataSegmentLength ๐Ÿ“

For this PDU, TotalAHSLength and DataSegmentLength MUST be 0. Simple, but very important, so don't skip it ๐Ÿ™ˆ.

10.7. SCSI Data-Out & SCSI Data-In ๐ŸŽ€

Okay so here's the shape of the SCSI Data-Out PDU, the one that carries WRITE data ๐Ÿ“ฆ

   Byte/     0       |       1       |       2       |       3       |
      /              |               |               |               |
     |0 1 2 3 4 5 6 7|0 1 2 3 4 5 6 7|0 1 2 3 4 5 6 7|0 1 2 3 4 5 6 7|
     +---------------+---------------+---------------+---------------+
    0|.|.| 0x05      |F| Reserved                                    |
     +---------------+---------------+---------------+---------------+
    4|TotalAHSLength | DataSegmentLength                             |
     +---------------+---------------+---------------+---------------+
    8| LUN or Reserved                                               |
     +                                                               +
   12|                                                               |
     +---------------+---------------+---------------+---------------+
   16| Initiator Task Tag                                            |
     +---------------+---------------+---------------+---------------+
   20| Target Transfer Tag or 0xffffffff                             |
     +---------------+---------------+---------------+---------------+
   24| Reserved                                                      |
     +---------------+---------------+---------------+---------------+
   28| ExpStatSN                                                     |
     +---------------+---------------+---------------+---------------+
   32| Reserved                                                      |
     +---------------+---------------+---------------+---------------+
   36| DataSN                                                        |
     +---------------+---------------+---------------+---------------+
   40| Buffer Offset                                                 |
     +---------------+---------------+---------------+---------------+
   44| Reserved                                                      |
     +---------------+---------------+---------------+---------------+
   48| Header-Digest (Optional)                                      |
     +---------------+---------------+---------------+---------------+
     / DataSegment                                                   /
    +/                                                               /
     +---------------+---------------+---------------+---------------+
     | Data-Digest (Optional)                                        |
     +---------------+---------------+---------------+---------------+

And here's the Data-In PDU for READ operations, which is the one coming back the other way ๐Ÿ’–

   Byte/     0       |       1       |       2       |       3       |
      /              |               |               |               |
     |0 1 2 3 4 5 6 7|0 1 2 3 4 5 6 7|0 1 2 3 4 5 6 7|0 1 2 3 4 5 6 7|
     +---------------+---------------+---------------+---------------+
    0|.|.| 0x25      |F|A|0 0 0|O|U|S| Reserved      |Status or Rsvd |
     +---------------+---------------+---------------+---------------+
    4|TotalAHSLength | DataSegmentLength                             |
     +---------------+---------------+---------------+---------------+
    8| LUN or Reserved                                               |
     +                                                               +
   12|                                                               |
     +---------------+---------------+---------------+---------------+
   16| Initiator Task Tag                                            |
     +---------------+---------------+---------------+---------------+
   20| Target Transfer Tag or 0xffffffff                             |
     +---------------+---------------+---------------+---------------+
   24| StatSN or Reserved                                            |
     +---------------+---------------+---------------+---------------+
   28| ExpCmdSN                                                      |
     +---------------+---------------+---------------+---------------+
   32| MaxCmdSN                                                      |
     +---------------+---------------+---------------+---------------+
   36| DataSN                                                        |
     +---------------+---------------+---------------+---------------+
   40| Buffer Offset                                                 |
     +---------------+---------------+---------------+---------------+
   44| Residual Count                                                |
     +---------------+---------------+---------------+---------------+
   48| Header-Digest (Optional)                                      |
     +---------------+---------------+---------------+---------------+
     / DataSegment                                                   /
    +/                                                               /
     +---------------+---------------+---------------+---------------+
     | Data-Digest (Optional)                                        |
     +---------------+---------------+---------------+---------------+

Status can ride along on the last Data-In PDU, as long as the command didn't end with an exception. That means the status is "good status" (GOOD, CONDITION MET, or INTERMEDIATE CONDITION MET) ๐ŸŒธ The S flag bit is how you signal that status (plus a residual count) is in there. Targets MAY choose to send even non-exception status in separate responses, but initiators MUST support non-exception status in Data-In PDUs ๐Ÿ’…


10.7.1. F (Final) Bit ๐Ÿฆ‹

For outgoing data, F is 1 on the last PDU of unsolicited data, or on the last PDU of a sequence that answers an R2T ๐ŸŽ€

Incoming (read) data works differently. F is 1 on the last input data PDU of each sequence. Input can be chopped into several sequences, and each one gets its own F bit. Splitting the stream like that doesn't change how DataSN is counted on Data-In PDUs ๐Ÿ”ข F MAY be used as a "change direction" flag for Bidirectional operations that need that kind of switch.

Size limits, bestie ๐Ÿ“ DataSegmentLength MUST not exceed MaxRecvDataSegmentLength for whichever direction it's going. The total DataSegmentLength across all PDUs in one sequence MUST not exceed MaxBurstLength (or FirstBurstLength for unsolicited data). BUT the number of PDUs can still go higher than the MaxBurstLength (or FirstBurstLength) to MaxRecvDataSegmentLength ratio, since the sender might only be able to make smaller PDUs ๐Ÿ™ˆ Using DataSegmentLength of 0 can blow the PDU count past what's reasonable, so you should totally avoid it ๐Ÿ’€

For Bidirectional ops, F is 1 at the end of both the input sequences and the output sequences ๐Ÿ”„


10.7.2. A (Acknowledge) Bit ๐Ÿ’œ

For sessions with ErrorRecoveryLevel 1 or higher, the target sets A to 1 when it wants a positive acknowledgement from the initiator for the data it received ๐Ÿ’Œ The target should use A moderately. It MAY set A to 1 only once every MaxBurstLength bytes, or on the last Data-In PDU that wraps up the entire requested read transfer for the task (from the target's point of view). It MUST NOT do it more often than that ๐Ÿ™… For sessions with ErrorRecoveryLevel=0, the target MUST NOT set A to 1, and the initiator MUST ignore A set to 1 there โœจ

On receiving a Data-In PDU with A set to 1 on a session with ErrorRecoveryLevel greater than 0, here's the deal ๐Ÿ‘‘ If there are no holes in the read data up to that Data-In PDU, the initiator MUST issue a SNACK of type DataACK. The one exception is when it can acknowledge the status for the task right away through ExpStatSN on other outbound PDUs, as long as the status for the task has also arrived. In that latter case (acknowledgement through ExpStatSN), sending a SNACK of type DataACK in response to the A bit is OPTIONAL. But if you do send it, it must not be sent after the status acknowledgement through ExpStatSN ๐Ÿซถ If the initiator has spotted holes in the read data before that Data-In PDU, it MUST postpone the SNACK of type DataACK until those holes are filled. An initiator also MUST NOT acknowledge the status for the task before the holes are filled ๐Ÿ˜ญ A status acknowledgement for a task that generated the Data-In PDUs counts as an implicit acknowledgement of those Data-In PDUs from the target's side, if the target asked for one ๐ŸŒˆ


10.7.3. Flags (byte 1) ๐Ÿšฉ

The last SCSI Data packet a target sends to an initiator for a SCSI command that finished successfully (with a status of GOOD, CONDITION MET, INTERMEDIATE, or INTERMEDIATE CONDITION MET) can optionally carry the Status for the data transfer too โญ Sense Data can't ride along with the Command Status, though. So if the command ends in an error, the response and sense data MUST be sent in a SCSI Response PDU (i.e., MUST NOT be sent in a SCSI Data packet). If Status comes along with the data, then a SCSI Response PDU MUST NOT be sent, because that would break SCSI rules (a single status) ๐Ÿ™… For Bidirectional commands, the status MUST be sent in a SCSI Response PDU ๐Ÿ”ฅ

Here's the bit breakdown for byte 1:

      bit 2-4 - Reserved.

      bit 5-6 - used the same as in a SCSI Response.  These bits are
                only valid when S is set to 1.  For details see Section
                10.4.1 Flags (byte 1).

      bit 7 S (status)- set to indicate that the Command Status field
                contains status.  If this bit is set to 1, the F bit
                MUST also be set to 1.

The StatSN, Status, and Residual Count fields only mean something if the S bit is set to 1. Their values are defined in Section 10.4 SCSI Response ๐Ÿงš


10.7.4. Target Transfer Tag and LUN ๐Ÿท

On outgoing data, the Target Transfer Tag gets handed to the target if the transfer is honoring an R2T. In that case, the Target Transfer Tag field is a replica of the Target Transfer Tag that came with the R2T ๐Ÿ”

On incoming data, the Target Transfer Tag and LUN MUST be provided by the target if A is set to 1. Otherwise, they're reserved ๐ŸŒธ The initiator copies the Target Transfer Tag and LUN into the SNACK of type DataACK it sends back after receiving a SCSI Data-In PDU with A set to 1 ๐Ÿ“ฌ

The Target Transfer Tag values aren't specified by this protocol, except that 0xffffffff is reserved and means the Target Transfer Tag isn't supplied ๐Ÿ™ˆ If the Target Transfer Tag is provided, then the LUN field MUST hold a valid value and be consistent with whatever was specified with the command. Otherwise, the LUN field is reserved ๐Ÿ“


10.7.5. DataSN ๐Ÿ”ข

For input (read) or bidirectional Data-In PDUs, DataSN is the input PDU number within the data transfer for the command identified by the Initiator Task Tag ๐Ÿ’–

R2T and Data-In PDUs share the same numbering sequence when we're talking about bidirectional commands (see Section 3.2.2.3 Data Sequencing) ๐Ÿฆ‹

For output (write) data PDUs, DataSN is the Data-Out PDU number within the current output sequence. That output sequence is identified either by the Initiator Task Tag (for unsolicited data), or it's a data sequence generated for one R2T (for data solicited through R2T) ๐Ÿ’…

10.7.6. Buffer Offset โœจ

okay so the Buffer Offset field is basically the "where am I in the big picture" ๐Ÿ—บ๏ธ marker. It holds the offset of this PDU's payload data inside the complete data transfer. The sum of the buffer offset and length should not exceed the expected transfer length for the command, bestie, so no going past the end of the transfer ๐Ÿ™…โ€โ™€๏ธ

The order of data PDUs inside a sequence is decided by DataPDUInOrder. When it's set to Yes, the PDUs have to show up in increasing Buffer Offset order, and overlays are forbidden. No overlapping, no drama ๐Ÿ’…

The ordering between sequences is decided by DataSequenceInOrder. When that's set to Yes, the sequences have to be in increasing Buffer Offset order, and overlays are forbidden here too ๐ŸŒธ

10.7.7. DataSegmentLength ๐ŸŽ€

This is the data payload length of a SCSI Data-In or SCSI Data-Out PDU. Sending 0 length data segments should be avoided, but initiators and targets MUST be able to properly receive 0 length data segments. So you can avoid them, but you have to be able to handle them if they show up ๐Ÿซถ

The Data Segments of Data-In and Data-Out PDUs SHOULD be filled to the integer number of 4 byte words (real payload) unless the F bit is set to 1. In other words, pad it out to 4 byte words, unless the F bit says otherwise ๐Ÿฆ‹

10.8. Ready To Transfer (R2T) ๐Ÿ”ฅ

This is the R2T PDU, and it's giving "the target gets to pick the vibe" ๐Ÿ‘‘ Here's the layout:

   Byte/     0       |       1       |       2       |       3       |
      /              |               |               |               |
     |0 1 2 3 4 5 6 7|0 1 2 3 4 5 6 7|0 1 2 3 4 5 6 7|0 1 2 3 4 5 6 7|
     +---------------+---------------+---------------+---------------+
    0|.|.| 0x31      |1| Reserved                                    |
     +---------------+---------------+---------------+---------------+
    4|TotalAHSLength | DataSegmentLength                             |
     +---------------+---------------+---------------+---------------+
    8| LUN                                                           |
     +                                                               +
   12|                                                               |
     +---------------+---------------+---------------+---------------+
   16| Initiator Task Tag                                            |
     +---------------+---------------+---------------+---------------+
   20| Target Transfer Tag                                           |
     +---------------+---------------+---------------+---------------+
   24| StatSN                                                        |
     +---------------+---------------+---------------+---------------+
   28| ExpCmdSN                                                      |
     +---------------+---------------+---------------+---------------+
   32| MaxCmdSN                                                      |
     +---------------+---------------+---------------+---------------+
   36| R2TSN                                                         |
     +---------------+---------------+---------------+---------------+
   40| Buffer Offset                                                 |
     +---------------+---------------+---------------+---------------+
   44| Desired Data Transfer Length                                  |
     +---------------------------------------------------------------+
   48| Header-Digest (Optional)                                      |
     +---------------+---------------+---------------+---------------+

When an initiator has submitted a SCSI Command with data that passes from the initiator to the target (WRITE), the target may specify which blocks of data it is ready to receive. The target may request that the data blocks be delivered in whichever order is convenient for the target at that particular instant. This information is passed from the target to the initiator in the Ready To Transfer (R2T) PDU ๐Ÿ’–

In order to allow write operations without an explicit initial R2T, the initiator and target MUST have negotiated the key InitialR2T to No during Login. So that's the setup step, and it's not optional ๐Ÿ“

An R2T MAY be answered with one or more SCSI Data-Out PDUs with a matching Target Transfer Tag. If an R2T is answered with a single Data-Out PDU, the Buffer Offset in the Data PDU MUST be the same as the one specified by the R2T, and the data length of the Data PDU MUST be the same as the Desired Data Transfer Length specified in the R2T. If the R2T is answered with a sequence of Data PDUs, the Buffer Offset and Length MUST be within the range of those specified by R2T, and the last PDU MUST have the F bit set to 1. If the last PDU (marked with the F bit) is received before the Desired Data Transfer Length is transferred, a target MAY choose to Reject that PDU with "Protocol error" reason code. ๐Ÿ™ˆ

DataPDUInOrder governs the Data-Out PDU ordering. If DataPDUInOrder is set to Yes, the Buffer Offsets and Lengths for consecutive PDUs MUST form a continuous non-overlapping range and the PDUs MUST be sent in increasing offset order. Lined up, no gaps, no overlaps ๐Ÿงš

The target may send several R2T PDUs. It, therefore, can have a number of pending data transfers. The number of outstanding R2T PDUs are limited by the value of the negotiated key MaxOutstandingR2T. Within a connection, outstanding R2Ts MUST be fulfilled by the initiator in the order in which they were received. First come, first served, literally ๐Ÿ’…

R2T PDUs MAY also be used to recover Data Out PDUs. Such an R2T (Recovery-R2T) is generated by a target upon detecting the loss of one or more Data-Out PDUs due to:

  • Digest error
  • Sequence error
  • Sequence reception timeout

A Recovery-R2T carries the next unused R2TSN, but requests part of or the entire data burst that an earlier R2T (with a lower R2TSN) had already requested. It's the comeback arc ๐ŸŒˆ

DataSequenceInOrder governs the buffer offset ordering in consecutive R2Ts. If DataSequenceInOrder is Yes, then consecutive R2Ts MUST refer to continuous non-overlapping ranges except for Recovery-R2Ts. โœจ

10.8.1. TotalAHSLength and DataSegmentLength ๐Ÿ“

For this PDU TotalAHSLength and DataSegmentLength MUST be 0. Zeros all the way, bestie ๐Ÿซถ

10.8.2. R2TSN ๐Ÿ’œ

R2TSN is the R2T PDU input PDU number within the command identified by the Initiator Task Tag. It's basically the R2T's numbered ticket for that command ๐ŸŽŸ๏ธ

For bidirectional commands R2T and Data-In PDUs share the input PDU numbering sequence (see Section 3.2.2.3 Data Sequencing). Same numbering line, shared between both ๐Ÿฆ‹

10.8.3. StatSN ๐ŸŒธ

The StatSN field will contain the next StatSN. The StatSN for this connection is not advanced after this PDU is sent. So this one doesn't move the counter forward, it just carries the next number ๐Ÿ™ƒ

10.8.4. Desired Data Transfer Length and Buffer Offset ๐ŸŽ€

The target specifies how many bytes it wants the initiator to send because of this R2T PDU. The target may request the data from the initiator in several chunks, not necessarily in the original order of the data. The target, therefore, also specifies a Buffer Offset that indicates the point at which the data transfer should begin, relative to the beginning of the total data transfer. The Desired Data Transfer Length MUST NOT be 0 and MUST not exceed MaxBurstLength. So it's the ask, the start point, and the size cap, all in one ๐Ÿ‘‘

10.8.5. Target Transfer Tag ๐Ÿงš

The target assigns its own tag to each R2T request that it sends to the initiator. This tag can be used by the target to easily identify the data it receives. The Target Transfer Tag and LUN are copied in the outgoing data PDUs and are only used by the target. There is no protocol rule about the Target Transfer Tag except that the value 0xffffffff is reserved and MUST NOT be sent by a target in an R2T. That one reserved value is off-limits, babe ๐Ÿšซ

10.9. Asynchronous Message ๐Ÿ“ฃ

An Asynchronous Message may be sent from the target to the initiator without correspondence to a particular command. The target specifies the reason for the event and sense data. It's the target just tapping the initiator on the shoulder, unprompted ๐Ÿ‘€

   Byte/     0       |       1       |       2       |       3       |
      /              |               |               |               |
     |0 1 2 3 4 5 6 7|0 1 2 3 4 5 6 7|0 1 2 3 4 5 6 7|0 1 2 3 4 5 6 7|
     +---------------+---------------+---------------+---------------+
    0|.|.| 0x32      |1| Reserved                                    |
     +---------------+---------------+---------------+---------------+
    4|TotalAHSLength | DataSegmentLength                             |
     +---------------+---------------+---------------+---------------+
    8| LUN or Reserved                                               |
     +                                                               +
   12|                                                               |
     +---------------+---------------+---------------+---------------+
   16| 0xffffffff                                                    |
     +---------------+---------------+---------------+---------------+
   20| Reserved                                                      |
     +---------------+---------------+---------------+---------------+
   24| StatSN                                                        |
     +---------------+---------------+---------------+---------------+
   28| ExpCmdSN                                                      |
     +---------------+---------------+---------------+---------------+
   32| MaxCmdSN                                                      |
     +---------------+---------------+---------------+---------------+
   36| AsyncEvent    | AsyncVCode    | Parameter1 or Reserved        |
     +---------------+---------------+---------------+---------------+
   40| Parameter2 or Reserved        | Parameter3 or Reserved        |
     +---------------+---------------+---------------+---------------+
   44| Reserved                                                      |
     +---------------+---------------+---------------+---------------+
   48| Header-Digest (Optional)                                      |
     +---------------+---------------+---------------+---------------+
     / DataSegment - Sense Data and iSCSI Event Data                 /
    +/                                                               /
     +---------------+---------------+---------------+---------------+
     | Data-Digest (Optional)                                        |
     +---------------+---------------+---------------+---------------+

Some Asynchronous Messages are strictly related to iSCSI while others are related to SCSI [SAM2]. Some are iSCSI tea, some are SCSI tea โ˜•

StatSN counts this PDU as an acknowledgeable event (StatSN is advanced), which allows for initiator and target state synchronization. So this one does move the StatSN forward, and that's how both sides stay in sync ๐Ÿ”ฅ

10.9.1. AsyncEvent โœจ

okay so the codes for iSCSI Asynchronous Messages (events) are: ๐Ÿ’–

  • 0: a SCSI Asynchronous Event gets reported in the sense data ๐Ÿ”. The Sense Data that rides along in the data segment tells you what the condition is. Sending a SCSI Event (Asynchronous Event Reporting in SCSI terminology) depends on whether the target supports SCSI asynchronous event reporting (see [SAM2]), which shows up in the standard INQUIRY data (see [SPC3]). Its use may be enabled by parameters in the SCSI Control mode page (see [SPC3]).
  • 1: target requests Logout ๐Ÿšช this one is serious, bestie ๐Ÿ˜ญ
    • This Async Message MUST be sent on the same connection as the one requesting to be logged out.
    • The initiator MUST honor this request by issuing a Logout as early as possible, but no later than Parameter3 seconds.
    • Initiator MUST send a Logout with a reason code of "Close the connection" OR "Close the session" to close all the connections.
    • Once this message is received, the initiator SHOULD NOT issue new iSCSI commands on the connection to be logged out.
    • The target MAY reject any new I/O requests that it receives after this Message with the reason code "Waiting for Logout".
    • If the initiator does not Logout in Parameter3 seconds, the target should send an Async PDU with iSCSI event code "Dropped the connection" if possible, or simply terminate the transport connection.
    • Parameter1 and Parameter2 are reserved.
  • 2: target says it's going to drop the connection ๐Ÿ‘‹
    • The Parameter1 field tells you the CID of the connection that is going to be dropped.
    • The Parameter2 field (Time2Wait) tells you, in seconds, the minimum time to wait before attempting to reconnect or reassign.
    • The Parameter3 field (Time2Retain) tells you the maximum time allowed to reassign commands after the initial wait (in Parameter2).
    • If the initiator does not attempt to reconnect and/or reassign the outstanding commands within the time specified by Parameter3, or if Parameter3 is 0, the target will terminate all outstanding commands on this connection. In this case, no other responses should be expected from the target for the outstanding commands on this connection.
    • A value of 0 for Parameter2 means reconnect can be attempted immediately ๐Ÿƒโ€โ™€๏ธ
  • 3: target says it's going to drop ALL the connections of this session ๐Ÿ’€
    • The Parameter1 field is reserved.
    • The Parameter2 field (Time2Wait) tells you, in seconds, the minimum time to wait before attempting to reconnect. The Parameter3 field (Time2Retain) tells you the maximum time allowed to reassign commands after the initial wait (in Parameter2).
    • If the initiator does not attempt to reconnect and/or reassign the outstanding commands within the time specified by Parameter3, or if Parameter3 is 0, the session is terminated.
    • In this case, the target will terminate all outstanding commands in this session; no other responses should be expected from the target for the outstanding commands in this session. A value of 0 for Parameter2 means reconnect can be attempted immediately.
  • 4: target requests parameter negotiation on this connection ๐Ÿ—ฃ๏ธ
    • The initiator MUST honor this request by issuing a Text Request (that can be empty) on the same connection as early as possible, but no later than Parameter3 seconds, unless a Text Request is already pending on the connection, or by issuing a Logout Request.
    • If the initiator does not issue a Text Request, the target may reissue the Asynchronous Message requesting parameter negotiation.
  • 255: vendor specific iSCSI Event ๐ŸŽ€ the AsyncVCode spills the vendor code tea, and data MAY accompany the report.

All other event codes are reserved. ๐Ÿ™…โ€โ™€๏ธ

10.9.2. AsyncVCode ๐ŸŽ€

AsyncVCode is a vendor specific detail code, and it's only valid if the AsyncEvent field says it's a vendor specific event. Otherwise, it is reserved. ๐Ÿ’…

10.9.3. LUN ๐Ÿงš

The LUN field MUST be valid if AsyncEvent is 0. Otherwise, this field is reserved. ๐Ÿซถ

10.9.4. Sense Data and iSCSI Event Data ๐Ÿ”

For a SCSI event, this data comes along with the report in the data segment and identifies the condition.

For an iSCSI event, additional vendor-unique data MAY accompany the Async event. Initiators MAY ignore the data when they don't understand it while processing the rest of the PDU ๐Ÿ™ˆ

If the DataSegmentLength is not 0, the format of the DataSegment looks like this:

Byte/     0       |       1       |       2       |       3       |
   /              |               |               |               |
  |0 1 2 3 4 5 6 7|0 1 2 3 4 5 6 7|0 1 2 3 4 5 6 7|0 1 2 3 4 5 6 7|
  +---------------+---------------+---------------+---------------+
 0|SenseLength                    | Sense Data                    |
  +---------------+---------------+---------------+---------------+
 x/ Sense Data                                                    /
  +---------------+---------------+---------------+---------------+
 y/ iSCSI Event Data                                              /
  /                                                               /
  +---------------+---------------+---------------+---------------+
 z|

10.9.4.1. SenseLength ๐ŸŒธ

This is the length of Sense Data. When the Sense Data field is empty (e.g., the event is not a SCSI event), SenseLength is 0.

10.10. Text Request ๐Ÿ’œ

The Text Request is here to swap info and to leave room for future extensions ๐Ÿฆ‹ It lets the initiator tell a target about its capabilities or ask for some special operations.

Byte/     0       |       1       |       2       |       3       |
   /              |               |               |               |
  |0 1 2 3 4 5 6 7|0 1 2 3 4 5 6 7|0 1 2 3 4 5 6 7|0 1 2 3 4 5 6 7|
  +---------------+---------------+---------------+---------------+
 0|.|I| 0x04      |F|C| Reserved                                  |
  +---------------+---------------+---------------+---------------+
 4|TotalAHSLength | DataSegmentLength                             |
  +---------------+---------------+---------------+---------------+
 8| LUN or Reserved                                               |
  +                                                               +
12|                                                               |
  +---------------+---------------+---------------+---------------+
16| Initiator Task Tag                                            |
  +---------------+---------------+---------------+---------------+
20| Target Transfer Tag or 0xffffffff                             |
  +---------------+---------------+---------------+---------------+
24| CmdSN                                                         |
  +---------------+---------------+---------------+---------------+
28| ExpStatSN                                                     |
  +---------------+---------------+---------------+---------------+
32/ Reserved                                                      /
 +/                                                               /
  +---------------+---------------+---------------+---------------+
48| Header-Digest (Optional)                                      |
  +---------------+---------------+---------------+---------------+
  / DataSegment (Text)                                            /
 +/                                                               /
  +---------------+---------------+---------------+---------------+
  | Data-Digest (Optional)                                        |
  +---------------+---------------+---------------+---------------+

An initiator MUST have at most one outstanding Text Request on a connection at any given time. ๐Ÿšฆ

On a connection failure, an initiator must either explicitly abort any active allegiant text negotiation task, or must cause such a task to be implicitly terminated by the target. ๐Ÿ’€

10.10.1. F (Final) Bit ๐Ÿ

When set to 1, this tells you it's the last or only text request in a sequence of Text Requests. Otherwise, it means more Text Requests will follow ๐Ÿซถ

10.10.2. C (Continue) Bit โœ‚๏ธ

When set to 1, this means the text (the set of key=value pairs) in this Text Request isn't complete yet, and it'll be continued in the subsequent Text Requests. Otherwise, it means this Text Request ends a set of key=value pairs. A Text Request with the C bit set to 1 MUST have the F bit set to 0. ๐Ÿ™…โ€โ™€๏ธ

10.10.3. Initiator Task Tag ๐Ÿท๏ธ

This is the initiator assigned identifier for this Text Request. If the command is sent as part of a sequence of text requests and responses, the Initiator Task Tag MUST be the same for all the requests within the sequence (kind of like linked SCSI commands ๐Ÿ”—). The I bit for all requests in a sequence also MUST be the same.

10.10.4. Target Transfer Tag ๐Ÿฆ‹

When the Target Transfer Tag is set to the reserved value 0xffffffff, it tells the target that this is a brand new request, and the target resets any internal state tied to the Initiator Task Tag (aka it resets the current negotiation state) ๐Ÿงน

The target sets the Target Transfer Tag in a text response to a value other than the reserved value 0xffffffff whenever it says it has more data to send or more operations to perform that are tied to the specified Initiator Task Tag. It MUST do so whenever it sets the F bit to 0 in the response. By copying the Target Transfer Tag from the response into the next Text Request, the initiator tells the target to keep going with the operation for that specific Initiator Task Tag ๐Ÿ” The initiator MUST ignore the Target Transfer Tag in the Text Response when the F bit is set to 1.

This mechanism lets the initiator and target pass a big pile of textual data over a sequence of text-command/text-response exchanges, or run extended negotiation sequences. It's giving teamwork ๐Ÿค

If the Target Transfer Tag is not 0xffffffff, the LUN field MUST be sent by the target in the Text Response. ๐Ÿงš

A target MAY reset its internal negotiation state if an exchange is stalled by the initiator for a long time, or if it is running out of resources. ๐Ÿ˜ฎโ€๐Ÿ’จ

Long text responses go like this:

  I->T Text SendTargets=All (F=1,TTT=0xffffffff)
  T->I Text <part 1> (F=0,TTT=0x12345678)
  I->T Text <empty> (F=1, TTT=0x12345678)
  T->I Text <part 2> (F=0, TTT=0x12345678)
  I->T Text <empty> (F=1, TTT=0x12345678)
  ...
  T->I Text <part n> (F=1, TTT=0xffffffff)

10.10.5. Text โœจ

Okay bestie, the data in a text request MUST NOT go over the target's MaxRecvDataSegmentLength. That's a per-connection, per-direction number that both sides negotiate, so nobody gets surprised ๐Ÿ’…. The text format is in Section 5.2 Text Mode Negotiation ๐ŸŽ€.

Chapters 11 and 12 list some basic Text key=value pairs. Some of them work in Login Request/Response and some work in Text Request/Response ๐ŸŒธ.

A key=value pair can span Text request or response boundaries. It can START in one PDU and finish in the next one ๐Ÿ˜ญ. So the end of a PDU doesn't automatically mean the key=value pair is over, iykyk.

The target responds by sending its response back to the initiator. The response text looks pretty similar to the request text โœจ. The text response MAY refer to key=value pairs from an earlier text request, and the request text may refer back to earlier responses too ๐Ÿฆ‹.

Chapter 5 has all the rules for Text Requests and Responses ๐Ÿ“–.

Text operations are usually for parameter setting and negotiations, but they can also run some long-lasting operations ๐Ÿ’œ. If something takes a long time, it should go in its own Text request ๐Ÿ“.


10.11. Text Response ๐Ÿงš

The Text Response PDU carries the target's answers to the initiator's Text request. The Text field matches the format of the Text request ๐Ÿ’–.

Byte/     0       |       1       |       2       |       3       |
   /              |               |               |               |
  |0 1 2 3 4 5 6 7|0 1 2 3 4 5 6 7|0 1 2 3 4 5 6 7|0 1 2 3 4 5 6 7|
  +---------------+---------------+---------------+---------------+
 0|.|.| 0x24      |F|C| Reserved                                  |
  +---------------+---------------+---------------+---------------+
 4|TotalAHSLength | DataSegmentLength                             |
  +---------------+---------------+---------------+---------------+
 8| LUN or Reserved                                               |
  +                                                               +
12|                                                               |
  +---------------+---------------+---------------+---------------+
16| Initiator Task Tag                                            |
  +---------------+---------------+---------------+---------------+
20| Target Transfer Tag or 0xffffffff                             |
  +---------------+---------------+---------------+---------------+
24| StatSN                                                        |
  +---------------+---------------+---------------+---------------+
28| ExpCmdSN                                                      |
  +---------------+---------------+---------------+---------------+
32| MaxCmdSN                                                      |
  +---------------+---------------+---------------+---------------+
36/ Reserved                                                      /
 +/                                                               /
  +---------------+---------------+---------------+---------------+
48| Header-Digest (Optional)                                      |
  +---------------+---------------+---------------+---------------+
  / DataSegment (Text)                                            /
 +/                                                               /
  +---------------+---------------+---------------+---------------+
  | Data-Digest (Optional)                                        |
  +---------------+---------------+---------------+---------------+

10.11.1. F (Final) Bit ๐Ÿ‘‘

When it's set to 1 in response to a Text Request that also has the Final bit set to 1, the F bit tells you the target is totally done with the whole operation ๐ŸŽ‰. If it's set to 0 in response to a Text Request with the Final Bit set to 1, the target has more work to do and is basically inviting a follow-on text request ๐Ÿ’…. A Text Response with the F bit set to 1 in response to a Text Request with the F bit set to 0 is a protocol error ๐Ÿ’€.

A Text Response with the F bit set to 1 MUST NOT contain key=value pairs that may require additional answers from the initiator ๐Ÿ™ˆ.

A Text Response with the F bit set to 1 MUST have a Target Transfer Tag field set to the reserved value of 0xffffffff ๐ŸŒˆ.

A Text Response with the F bit set to 0 MUST have a Target Transfer Tag field set to a value other than the reserved 0xffffffff ๐Ÿฆ‹.

10.11.2. C (Continue) Bit ๐ŸŒธ

When it's set to 1, the text (the set of key=value pairs) in this Text Response isn't finished yet, and it'll keep going in later Text Responses. If it's 0, this Text Response wraps up a set of key=value pairs ๐Ÿ“. A Text Response with the C bit set to 1 MUST have the F bit set to 0 โœจ.

10.11.3. Initiator Task Tag ๐ŸŽ€

The Initiator Task Tag matches the tag from the original Text Request ๐Ÿ’œ.

10.11.4. Target Transfer Tag ๐Ÿ’–

Sometimes a target has more work to do. Maybe it can't send all the remaining text in one Text Response, or it has to keep negotiating. If it has enough resources to keep going, it MUST set the Target Transfer Tag to a value other than the reserved 0xffffffff. If not, the Target Transfer Tag MUST be set to 0xffffffff ๐Ÿ”ฅ.

When the Target Transfer Tag isn't 0xffffffff, the LUN field may be significant ๐Ÿงš.

The initiator MUST copy the Target Transfer Tag and LUN into its next request, which tells the target it wants the rest of the data ๐Ÿซถ.

When the target gets a Text Request with the Target Transfer Tag set to the reserved 0xffffffff, it resets its internal information (its state) for that Initiator Task Tag, which restarts the negotiation ๐Ÿ”„.

When a target can't finish the operation in a single Text Response and doesn't have enough resources to continue, it rejects the Text Request with the appropriate Reject code ๐Ÿ™…โ€โ™€๏ธ.

A target may reset its internal state for an Initiator Task Tag, meaning the current negotiation state that's expressed through the Target Transfer Tag, if the initiator stops continuing the exchange for a while ๐Ÿ˜ด. After that, the target may reject later Text Requests whose Target Transfer Tag is set to the "stale" value ๐Ÿฅ€.

10.11.5. StatSN ๐Ÿ“ˆ

The target's StatSN variable goes up by one for every Text Response it sends โญ.

10.11.6. Text Response Data ๐ŸŽ€

The data lengths of a text response MUST NOT exceed the iSCSI initiator MaxRecvDataSegmentLength. That's also a per-connection, per-direction negotiated parameter ๐Ÿ’….

The text in the Text Response Data follows the same rules as the text in the Text Request Data (see Section 10.10.5 Text) ๐Ÿ“.

Even though the initiator is the one asking and controls how the request-response starts and ends, the target can offer its own key=value pairs as part of a sequence, not just in response to the initiator ๐ŸŒˆ.


10.12. Login Request ๐Ÿ”

After a TCP connection is set up between an initiator and a target, the initiator MUST start a Login Phase so it can get further access to the target's resources ๐Ÿ—๏ธ.

The Login Phase (see Chapter 5) is a sequence of Login Requests and Responses that all carry the same Initiator Task Tag ๐Ÿงฉ.

Login Requests are always considered immediate โšก.

Byte/     0       |       1       |       2       |       3       |
   /              |               |               |               |
  |0 1 2 3 4 5 6 7|0 1 2 3 4 5 6 7|0 1 2 3 4 5 6 7|0 1 2 3 4 5 6 7|
  +---------------+---------------+---------------+---------------+
 0|.|1| 0x03      |T|C|.|.|CSG|NSG| Version-max   | Version-min   |
  +---------------+---------------+---------------+---------------+
 4|TotalAHSLength | DataSegmentLength                             |
  +---------------+---------------+---------------+---------------+
 8| ISID                                                          |
  +                               +---------------+---------------+
12|                               | TSIH                          |
  +---------------+---------------+---------------+---------------+
16| Initiator Task Tag                                            |
  +---------------+---------------+---------------+---------------+
20| CID                           | Reserved                      |
  +---------------+---------------+---------------+---------------+
24| CmdSN                                                         |
  +---------------+---------------+---------------+---------------+
28| ExpStatSN   or   Reserved                                     |
  +---------------+---------------+---------------+---------------+
32| Reserved                                                      |
  +---------------+---------------+---------------+---------------+
36| Reserved                                                      |
  +---------------+---------------+---------------+---------------+
40/ Reserved                                                      /
 +/                                                               /
  +---------------+---------------+---------------+---------------+
48/ DataSegment - Login Parameters in Text request Format         /
 +/                                                               /
  +---------------+---------------+---------------+---------------+

10.12.1. T (Transit) Bit ๐Ÿš€

If it's set to 1, the initiator is ready to move on to the next stage ๐Ÿƒโ€โ™€๏ธ.

If the T bit is set to 1 and NSG is FullFeaturePhase, that also means the initiator is ready for the Final Login Response (see Chapter 5) ๐ŸŽŠ.

10.12.2. C (Continue) Bit ๐ŸŒท

When it's set to 1, the text (the set of key=value pairs) in this Login Request isn't complete yet, and it'll continue in later Login Requests. Otherwise, this Login Request ends a set of key=value pairs ๐Ÿ’Œ. A Login Request with the C bit set to 1 MUST have the T bit set to 0 ๐Ÿ™…โ€โ™€๏ธ.

10.12.3. CSG and NSG ๐ŸŽญ

Through these fields, Current Stage (CSG) and Next Stage (NSG), the Login negotiation requests and responses get tied to a specific stage in the session (SecurityNegotiation, LoginOperationalNegotiation, FullFeaturePhase). They can also say which stage they want to move to next (see Chapter 5) ๐Ÿ—บ๏ธ. The next stage value only counts when the T bit is 1. Otherwise, it's reserved ๐Ÿซง.

The stage codes are:

  • 0 - SecurityNegotiation
  • 1 - LoginOperationalNegotiation
  • 3 - FullFeaturePhase

All other codes are reserved ๐Ÿ™ˆ.

10.12.4. Version ๐ŸŽ€

The version number of the current draft is 0x00. Because of that, all devices MUST carry 0x00 for both Version-min and Version-max ๐Ÿ’–.

10.12.4.1. Version-max ๐ŸŒˆ

This is the maximum Version number supported ๐Ÿ’œ.

All Login Requests within the Login Phase MUST carry the same Version-max ๐Ÿ”.

The target MUST use the value it got from the first Login Request ๐Ÿฅ‡.

10.12.4.2. Version-min ๐Ÿฆ‹

All Login Requests within the Login Phase MUST carry the same Version-min. The target MUST use the value it got from the first Login Request ๐Ÿซถ.

10.12.5. ISID โœจ

okay so ISID is the initiator-defined part of the session identifier, and it's giving very structured ๐ŸŽ€ (see [RFC3721] and Section 9.1.1 Conservative Reuse of ISIDs for the deep dive, bestie):

    Byte/     0       |       1       |       2       |       3       |
       /              |               |               |               |
      |0 1 2 3 4 5 6 7|0 1 2 3 4 5 6 7|0 1 2 3 4 5 6 7|0 1 2 3 4 5 6 7|
      +---------------+---------------+---------------+---------------+
     8| T |    A      |              B                |      C        |
      +---------------+---------------+---------------+---------------+
    12|               D               |
      +---------------+---------------+

the T field is the boss here ๐Ÿ‘‘ because it tells you what format and usage A, B, C, and D have:

     T

     00b     OUI-Format
             A&B are a 22 bit OUI
             (the I/G & U/L bits are omitted)
             C&D 24 bit qualifier
     01b     EN - Format (IANA Enterprise Number)
             A - Reserved
             B&C EN (IANA Enterprise Number)
             D - Qualifier
     10b     "Random"
             A - Reserved
             B&C Random
             D - Qualifier
     11b     A,B,C&D Reserved

for T being 00b or 01b, a combo of A and B (for 00b) or B and C (for 01b) tells you which vendor or organization's component (software or hardware) made this ISID ๐Ÿซถ and any vendor with one or more OUIs, or one or more Enterprise Numbers, MUST use at least one of those numbers and pick the right T value when its components generate ISIDs. the OUI or EN MUST be set in the matching fields in network byte order (byte big-endian), no cap ๐Ÿ’…

if T is 10b, then B and C get set to a random 24-bit unsigned integer in network byte order (byte big-endian). lowkey this is where [RFC3721] comes in clutch for "conservative reuse" ๐Ÿฆ‹

the Qualifier field is a 16 or 24-bit unsigned integer that gives you a range of ISID values inside the chosen namespace ๐ŸŒธ it may be set to any value that fits the rules in the iSCSI protocol (see Section 3.4.3 Consequences of the Model and Section 9.1.1 Conservative Reuse of ISIDs).

the T value of 11b is reserved, so don't even ๐Ÿ™ˆ

and now the big one: if the ISID comes from something a vendor assigned to a hardware adapter or interface as a preset default, it MUST be configurable to a value picked for the SCSI port behavior the system wants (see Section 9.1.1 Conservative Reuse of ISIDs and Section 9.1.2 iSCSI Name, ISID, and TPGT Use). the resulting ISID MUST also stay persistent over power cycles, reboot, card swap, etc. main character energy, but make it consistent ๐Ÿงš

10.12.6. TSIH ๐Ÿ’œ

TSIH must be set in the first Login Request, and the reserved value 0 MUST be used on the first connection for a new session. otherwise, the TSIH the target sent back at the end of a successful login on the first connection for this session MUST be used. basically the TSIH tells the target which existing session this new connection belongs to ๐ŸŽ€

all Login Requests within a Login Phase MUST carry the same TSIH. periodt ๐Ÿ’…

the target MUST check the value presented with the first Login Request and act as specified in Section 5.3.1 Login Phase Start โœจ

10.12.7. Connection ID - CID ๐Ÿฆ‹

this is a unique ID for this connection within the session. literally that's the whole vibe ๐ŸŒˆ

all Login Requests within the Login Phase MUST carry the same CID.

the target MUST use the value presented with the first Login Request.

โš ๏ธ okay this one is a whole plot twist: a Login Request with a non-zero TSIH and a CID that matches an existing connection implies a logout of that connection followed by a Login (see Section 5.3.4 Connection Reinstatement). for the details of that implicit Logout Request, check Section 10.14 Logout Request ๐Ÿ‘€

10.12.8. CmdSN ๐Ÿ“

CmdSN is either the initial command sequence number of a session (for the first Login Request of a session, aka the "leading" login), or the command sequence number in the command stream if the login is for a new connection in an existing session. ngl it's the number that keeps everything in order ๐Ÿ’–

examples, because you know I love a good example ๐Ÿ™ˆ

  • login on a leading connection: if the leading login carries the CmdSN 123, then every other Login Request in the same Login Phase carries the CmdSN 123, and the first non-immediate command in FullFeaturePhase also carries the CmdSN 123.
  • login on a connection that isn't leading: if the current CmdSN is 500 when the first login on the connection gets issued, then that PDU carries CmdSN=500. later Login Requests needed to finish this Login Phase may carry a CmdSN higher than 500 if non-immediate requests issued on other connections in the same session moved CmdSN forward. the timeline is a little messy, but it's valid ๐Ÿซถ

if the Login Request is a leading Login Request, the target MUST use the value presented in CmdSN as the target value for ExpCmdSN โœจ

10.12.9. ExpStatSN ๐ŸŒธ

for the first Login Request on a connection, this is the ExpStatSN for the old connection, and this field is only valid if the Login Request restarts a connection (see Section 5.3.4 Connection Reinstatement).

for subsequent Login Requests, it's used to acknowledge the Login Responses with their increasing StatSN values ๐ŸŽ€

10.12.10. Login Parameters ๐Ÿ‘‘

the initiator MUST provide some basic parameters so the target can figure out whether the initiator is allowed to use its resources, plus the initial text parameters for the security exchange. it's the VIP pass ๐ŸŽŸ๏ธ

all the rules in Section 10.10.5 Text for text requests also hold for Login Requests. the keys and their explanations are in Chapter 11 (security negotiation keys) and Chapter 12 (operational parameter negotiation keys). all keys in Chapter 12, except for the X extension formats, MUST be supported by iSCSI initiators and targets. keys in Chapter 11 only need to be supported when the function they refer to is mandatory to implement ๐Ÿ’…


10.13. Login Response ๐Ÿ’–

the Login Response is how everybody knows the progress and/or end of the Login Phase ๐Ÿงš here's the layout:

   Byte/     0       |       1       |       2       |       3       |
      /              |               |               |               |
     |0 1 2 3 4 5 6 7|0 1 2 3 4 5 6 7|0 1 2 3 4 5 6 7|0 1 2 3 4 5 6 7|
     +---------------+---------------+---------------+---------------+
    0|.|.| 0x23      |T|C|.|.|CSG|NSG| Version-max   | Version-active|
     +---------------+---------------+---------------+---------------+
    4|TotalAHSLength | DataSegmentLength                             |
     +---------------+---------------+---------------+---------------+
    8| ISID                                                          |
     +                               +---------------+---------------+
   12|                               | TSIH                          |
     +---------------+---------------+---------------+---------------+
   16| Initiator Task Tag                                            |
     +---------------+---------------+---------------+---------------+
   20| Reserved                                                      |
     +---------------+---------------+---------------+---------------+
   24| StatSN                                                        |
     +---------------+---------------+---------------+---------------+
   28| ExpCmdSN                                                      |
     +---------------+---------------+---------------+---------------+
   32| MaxCmdSN                                                      |
     +---------------+---------------+---------------+---------------+
   36| Status-Class  | Status-Detail | Reserved                      |
     +---------------+---------------+---------------+---------------+
   40/ Reserved                                                      /
    +/                                                               /
     +---------------+---------------+---------------+---------------+
   48/ DataSegment - Login Parameters in Text request Format         /
    +/                                                               /
     +---------------+---------------+---------------+---------------+

10.13.1. Version-max ๐Ÿฆ‹

this is the highest version number the target supports ๐ŸŒˆ

all Login Responses within the Login Phase MUST carry the same Version-max.

the initiator MUST use the value presented as a response to the first Login Request.

10.13.2. Version-active ๐Ÿ“

this one shows the highest version that both the target and initiator support ๐Ÿ’œ if the target doesn't support a version inside the range the initiator asked for, the target rejects the login, and this field shows the lowest version the target does support. so close, but no cigar ๐Ÿ™ˆ

all Login Responses within the Login Phase MUST carry the same Version-active.

the initiator MUST use the value presented as a response to the first Login Request.

10.13.3. TSIH โœจ

the TSIH is the target assigned session identifying handle ๐ŸŽ€ its internal format and content aren't defined by this protocol, except that the value 0 is reserved. except for the Login Final-Response in a new session, this field should be set to the TSIH the initiator gave in the Login Request. for a new session, the target MUST generate a non-zero TSIH and ONLY return it in the Login Final-Response (see Section 5.3 Login Phase) ๐Ÿ‘‘

10.13.4. StatSN ๐ŸŒธ

for the first Login Response (the response to the first Login Request), this is the starting status Sequence Number for the connection. the next response of any kind, including the next Login Response (if there is one) in the same Login Phase, will carry this number + 1. this field is only valid if the Status-Class is 0 ๐Ÿซถ bestie, it's all about keeping count โœจ

10.13.5. Status-Class and Status-Detail โœจ

okay so the Status in a Login Response tells you how the Login Phase went ๐Ÿ’… It has two parts:

Status-Class
Status-Detail

Status-Class 0 means success ๐ŸŽ€ that's the vibe we want.

A non-zero Status-Class means something went sideways ๐Ÿ’€ In that case, Status-Class is enough for a simple initiator to handle the exception, without even peeking at Status-Detail. Status-Detail is for the more sophisticated initiators who want finer-grained handling, plus better info for logging ๐Ÿงš

Here are the status classes:

  • 0 - Success - indicates that the iSCSI target successfully received, understood, and accepted the request. The numbering fields (StatSN, ExpCmdSN, MaxCmdSN) are only valid if Status-Class is 0.
  • 1 - Redirection - indicates that the initiator must take further action to complete the request. This is usually due to the target moving to a different address. All of the redirection status class responses MUST return one or more text key parameters of the type "TargetAddress", which indicates the target's new address. A redirection response MAY be issued by a target prior or after completing a security negotiation if a security negotiation is required. A redirection SHOULD be accepted by an initiator even without having the target complete a security negotiation if any security negotiation is required, and MUST be accepted by the initiator after the completion of the security negotiation if any security negotiation is required. ๐Ÿฆ‹
  • 2 - Initiator Error (not a format error) - indicates that the initiator most likely caused the error. This MAY be due to a request for a resource for which the initiator does not have permission. The request should not be tried again. ๐Ÿ™ˆ
  • 3 - Target Error - indicates that the target sees no errors in the initiator's Login Request, but is currently incapable of fulfilling the request. The initiator may re-try the same Login Request later. ๐ŸŒธ

The table below shows all of the currently allocated status codes. The codes are in hexadecimal. The first byte is the status class and the second byte is the status detail ๐Ÿ“

-----------------------------------------------------------------
Status        | Code | Description
              |(hex) |
-----------------------------------------------------------------
Success       | 0000 | Login is proceeding OK (*1).
-----------------------------------------------------------------
Target moved  | 0101 | The requested iSCSI Target Name (ITN)
temporarily   |      |  has temporarily moved
              |      |  to the address provided.
-----------------------------------------------------------------
Target moved  | 0102 | The requested ITN has permanently moved
permanently   |      |  to the address provided.
-----------------------------------------------------------------
Initiator     | 0200 | Miscellaneous iSCSI initiator
error         |      | errors.
----------------------------------------------------------------
Authentication| 0201 | The initiator could not be
failure       |      | successfully authenticated or target
              |      | authentication is not supported.
-----------------------------------------------------------------
Authorization | 0202 | The initiator is not allowed access
failure       |      | to the given target.
-----------------------------------------------------------------
Not found     | 0203 | The requested ITN does not
              |      | exist at this address.
-----------------------------------------------------------------
Target removed| 0204 | The requested ITN has been removed and
              |      |no forwarding address is provided.
-----------------------------------------------------------------
Unsupported   | 0205 | The requested iSCSI version range is
version       |      | not supported by the target.
-----------------------------------------------------------------
Too many      | 0206 | Too many connections on this SSID.
connections   |      |
-----------------------------------------------------------------
Missing       | 0207 | Missing parameters (e.g., iSCSI
parameter     |      | Initiator and/or Target Name).
-----------------------------------------------------------------
Can't include | 0208 | Target does not support session
in session    |      | spanning to this connection (address).
-----------------------------------------------------------------
Session type  | 0209 | Target does not support this type of
not supported |      | of session or not from this Initiator.
-----------------------------------------------------------------
Session does  | 020a | Attempt to add a connection
not exist     |      | to a non-existent session.
-----------------------------------------------------------------
Invalid during| 020b | Invalid Request type during Login.
login         |      |
-----------------------------------------------------------------
Target error  | 0300 | Target hardware or software error.
-----------------------------------------------------------------
Service       | 0301 | The iSCSI service or target is not
unavailable   |      | currently operational.
-----------------------------------------------------------------
Out of        | 0302 | The target has insufficient session,
resources     |      | connection, or other resources.
-----------------------------------------------------------------

(*1) If the response T bit is 1 in both the request and the matching response, and the NSG is FullFeaturePhase in both the request and the matching response, the Login Phase is finished and the initiator may proceed to issue SCSI commands. โญ

If the Status Class is not 0, the initiator and target MUST close the TCP connection. ๐Ÿ”ฅ

If the target wishes to reject the Login Request for more than one reason, it should return the primary reason for the rejection. ๐Ÿ’œ


10.13.6. T (Transit) bit ๐Ÿฆ‹

The T bit is set to 1 as an indicator of the end of the stage. If the T bit is set to 1 and NSG is FullFeaturePhase, then this is also the Final Login Response (see Chapter 5). A T bit of 0 indicates a "partial" response, which means "more negotiation needed" ๐ŸŒˆ

A Login Response with a T bit set to 1 MUST NOT contain key=value pairs that may require additional answers from the initiator within the same stage. ๐Ÿ™…โ€โ™€๏ธ

If the status class is 0, the T bit MUST NOT be set to 1 if the T bit in the request was set to 0. ๐Ÿ’…


10.13.7. C (Continue) Bit ๐ŸŽ€

When set to 1, this indicates that the text (set of key=value pairs) in this Login Response is not complete. It will be continued on subsequent Login Responses. Otherwise, it indicates that this Login Response ends a set of key=value pairs. A Login Response with the C bit set to 1 MUST have the T bit set to 0. ๐Ÿซถ


10.13.8. Login Parameters ๐Ÿ‘‘

The target MUST provide some basic parameters so the initiator can figure out if it's connected to the correct port, plus the initial text parameters for the security exchange ๐Ÿงš

All the rules specified in Section 10.11.6 Text Response Data for text responses also hold for Login Responses. Keys and their explanations are listed in Chapter 11 (security negotiation keys) and Chapter 12 (operational parameter negotiation keys). All keys in Chapter 12, except for the X extension formats, MUST be supported by iSCSI initiators and targets. Keys in Chapter 11 only need to be supported when the function to which they refer is mandatory to implement. โœจ

10.14. Logout Request โœจ

okay bestie, so the Logout Request is how you do a controlled closing of a connection. think graceful exit, no dramatic door slam ๐Ÿšช๐Ÿ’…

An initiator MAY use a Logout Request to remove a connection from a session or to close an entire session ๐Ÿงš

After sending the Logout Request PDU, an initiator MUST NOT send any new iSCSI requests on the closing connection. if the Logout Request is meant to close the session, new iSCSI requests MUST NOT be sent on any of the connections in that session ๐Ÿ™ˆ

when a target gets a Logout Request with the reason code "close the connection" or "close the session", the target MUST terminate all pending commands on that connection or session respectively, whether they were acknowledged via ExpCmdSN or not ๐Ÿ’€

when a target gets a Logout Request with the reason code "remove connection for recovery", the target MUST discard all requests that haven't been acknowledged via ExpCmdSN and were issued on the specified connection. it also has to suspend all data/status/R2T transfers on behalf of pending commands on that connection ๐Ÿ”ฅ

then the target issues the Logout Response and half-closes the TCP connection (sends FIN). after the initiator gets the Logout Response and tries to catch the FIN (if it still can), the initiator MUST completely close the logging-out connection. for the terminated commands, no additional responses should be expected ๐Ÿซถ

a Logout for a CID can happen on a different transport connection when the TCP connection for that CID has already been terminated. in that case, a Logout just implies a logical "closing" of the iSCSI connection for the CID ๐Ÿฆ‹

all commands that weren't terminated or completed (with status) and acknowledged when the connection is fully closed can be reassigned to a new connection if the target supports connection recovery โœจ

if an initiator wants to start recovery for a failing connection, it MUST use the Logout Request to "clean-up" the target end of the failing connection and let recovery start. it can also use the Login Request with a non-zero TSIH and the same CID on a new connection to get the same effect (see Section 10.14.3 CID). in sessions with a single connection, the connection can be closed and then a new one reopened. a connection reinstatement login can be used for recovery too (see Section 5.3.4 Connection Reinstatement) ๐ŸŒธ

ngl this is the part to re-read. the cleanup order is everything ๐Ÿ‘€

a successful Logout Request with the reason code "close the connection" or "remove the connection for recovery" makes the target discard unacknowledged commands that came in on the connection being logged out. these are commands that arrived on that connection but haven't been delivered to SCSI, because one or more commands with a smaller CmdSN haven't been received by iSCSI yet. see Section 3.2.2.1 Command Numbering and Acknowledging. the resulting holes in the command sequence numbers have to be handled by appropriate recovery (see Chapter 6), unless the session is also closed ๐Ÿงฉ

the whole logout discussion in this section also applies to an implicit Logout done through a connection reinstatement or session reinstatement. when a Login Request does an implicit Logout, that implicit Logout happens as if it had the reason codes below ๐Ÿ’œ

  Reason code        Type of implicit Logout
  -------------------------------------------
      0              session reinstatement
      1              connection reinstatement when
                    the operational ErrorRecoveryLevel < 2
      2              connection reinstatement when
                    the operational ErrorRecoveryLevel = 2

Here's the PDU layout, verbatim, because the bits matter ๐ŸŽ€

Byte/     0       |       1       |       2       |       3       |
   /              |               |               |               |
  |0 1 2 3 4 5 6 7|0 1 2 3 4 5 6 7|0 1 2 3 4 5 6 7|0 1 2 3 4 5 6 7|
  +---------------+---------------+---------------+---------------+
 0|.|I| 0x06      |1| Reason Code | Reserved                      |
  +---------------+---------------+---------------+---------------+
 4|TotalAHSLength | DataSegmentLength                             |
  +---------------------------------------------------------------+
 8/ Reserved                                                      /
 +/                                                               /
  +---------------+---------------+---------------+---------------+
16| Initiator Task Tag                                            |
  +---------------+---------------+---------------+---------------+
20| CID or Reserved               | Reserved                      |
  +---------------+---------------+---------------+---------------+
24| CmdSN                                                         |
  +---------------+---------------+---------------+---------------+
28| ExpStatSN                                                     |
  +---------------+---------------+---------------+---------------+
32/ Reserved                                                      /
 +/                                                               /
  +---------------+---------------+---------------+---------------+
48| Header-Digest (Optional)                                      |
  +---------------+---------------+---------------+---------------+

10.14.1. Reason Code

Reason Code tells you why the Logout is happening:

  • 0 - close the session. all commands associated with the session (if any) are terminated.
  • 1 - close the connection. all commands associated with the connection (if any) are terminated.
  • 2 - remove the connection for recovery. the connection is closed and all commands associated with it (if any) are to be prepared for a new allegiance.

All other values are reserved ๐ŸŒท

10.14.2. TotalAHSLength and DataSegmentLength

For this PDU, TotalAHSLength and DataSegmentLength MUST be 0. no extras, babe ๐Ÿซง

10.14.3. CID

This is the connection ID of the connection to be closed (including closing the TCP stream). this field is only valid if the reason code is not "close the session" ๐Ÿงš

10.14.4. ExpStatSN

This is the last ExpStatSN value for the connection to be closed ๐Ÿ“

10.14.5. Implicit termination of tasks

a target implicitly terminates the active tasks, thanks to the iSCSI protocol, in these cases:

  • a) When a connection is implicitly or explicitly logged out with the reason code of "Close the connection" and there are active tasks allegiant to that connection.
  • b) When a connection fails and eventually the connection state times out (state transition M1 in Section 7.2.2 State Transition Descriptions for Initiators and Targets) and there are active tasks allegiant to that connection.
  • c) When a successful recovery Logout is performed while there are active tasks allegiant to that connection, and those tasks eventually time out after the Time2Wait and Time2Retain periods without allegiance reassignment.
  • d) When a connection is implicitly or explicitly logged out with the reason code of "Close the session" and there are active tasks in that session.

if the tasks terminated in any of the above cases are SCSI tasks, they must be internally terminated as if with CHECK CONDITION status. this status only matters for handling the internal SCSI state and SCSI side effects around ordering, because it's never sent back to the initiator as a terminating status. however, extra actions might be needed at the SCSI level depending on the SCSI context defined by the SCSI standards (e.g., queued commands and ACA). in cases a), b), and c), after the tasks are terminated, the target MUST report a Unit Attention condition on the next command processed on any connection for each affected I_T_L nexus, with the status of CHECK CONDITION and the ASC/ASCQ value of 47h/7Fh - "SOME COMMANDS CLEARED BY ISCSI PROTOCOL EVENT" - etc. - see [SAM2] and [SPC3]). ngl this is a LOT of SCSI lore, but it's giving thorough ๐Ÿค“โœจ


10.15. Logout Response

The Logout Response is how the target tells you whether the cleanup for the connection(s) has finished ๐ŸŽ€

After Logout, the TCP connection referred to by the CID MUST be closed at both ends (or all connections must be closed if the logout reason was session close) ๐Ÿ”’

Here's the layout again, verbatim ๐Ÿฆ‹

Byte/     0       |       1       |       2       |       3       |
   /              |               |               |               |
  |0 1 2 3 4 5 6 7|0 1 2 3 4 5 6 7|0 1 2 3 4 5 6 7|0 1 2 3 4 5 6 7|
  +---------------+---------------+---------------+---------------+
 0|.|.| 0x26      |1| Reserved    | Response      | Reserved      |
  +---------------+---------------+---------------+---------------+
 4|TotalAHSLength | DataSegmentLength                             |
  +---------------------------------------------------------------+
 8/ Reserved                                                      /
 +/                                                               /
  +---------------+---------------+---------------+---------------+
16| Initiator Task Tag                                            |
  +---------------+---------------+---------------+---------------+
20| Reserved                                                      |
  +---------------+---------------+---------------+---------------+
24| StatSN                                                        |
  +---------------+---------------+---------------+---------------+
28| ExpCmdSN                                                      |
  +---------------+---------------+---------------+---------------+
32| MaxCmdSN                                                      |
  +---------------+---------------+---------------+---------------+
36| Reserved                                                      |
  +---------------------------------------------------------------+
40| Time2Wait                     | Time2Retain                   |
  +---------------+---------------+---------------+---------------+
44| Reserved                                                      |
  +---------------+---------------+---------------+---------------+
48| Header-Digest (Optional)                                      |
  +---------------+---------------+---------------+---------------+

10.15.1. Response

Logout Response values:

  • 0 - connection or session closed successfully.
  • 1 - CID not found.
  • 2 - connection recovery is not supported. this is for when the Logout reason code was recovery and the target doesn't support it, as indicated by the ErrorRecoveryLevel.
  • 3 - cleanup failed for various reasons.

10.15.2. TotalAHSLength and DataSegmentLength

For this PDU, TotalAHSLength and DataSegmentLength MUST be 0 ๐Ÿ’…

10.15.3. Time2Wait

If the Logout Response code is 0 and the operational ErrorRecoveryLevel is 2, this is the minimum amount of time, in seconds, to wait before attempting task reassignment. if the Logout Response code is 0 and the operational ErrorRecoveryLevel is less than 2, this field is to be ignored โณ

this field is invalid if the Logout Response code is 1 ๐Ÿ™…

if the Logout Response code is 2 or 3, this field specifies the minimum time to wait before attempting a new implicit or explicit logout โฐ

if Time2Wait is 0, the reassignment or a new Logout may be attempted immediately ๐Ÿš€

10.15.4. Time2Retain โœจ

okay so if the Logout response code is 0 and the operational ErrorRecoveryLevel is 2, this is the max amount of time (in seconds) the target waits, after the initial wait (Time2Wait), for the allegiance reassignment of any active task. Once that's up, the task state is discarded ๐Ÿ’€. If the Logout response code is 0 but the operational ErrorRecoveryLevel is less than 2, this field is to be ignored, bestie ๐Ÿซถ.

This field is invalid if the Logout response code is 1. Not it ๐Ÿ™…โ€โ™€๏ธ.

If the Logout response code is 2 or 3, this field is the max time (in seconds) the target waits, after Time2Wait, for a new implicit or explicit logout ๐Ÿฆ‹.

If it's the last connection of a session, the whole session state gets discarded after Time2Retain. main character energy, the session gets cleaned up last ๐Ÿ’…

If Time2Retain is 0, the target has already discarded the connection (and possibly the session) state along with the task states. So no reassignment or Logout is needed in this case ๐ŸŒธ.


10.16. SNACK Request ๐Ÿ“

Here's the diagram, copied exactly so nobody gets confused ๐Ÿงš:

   Byte/     0       |       1       |       2       |       3       |
      /              |               |               |               |
     |0 1 2 3 4 5 6 7|0 1 2 3 4 5 6 7|0 1 2 3 4 5 6 7|0 1 2 3 4 5 6 7|
     +---------------+---------------+---------------+---------------+
    0|.|.| 0x10      |1|.|.|.| Type  | Reserved                      |
     +---------------+---------------+---------------+---------------+
    4|TotalAHSLength | DataSegmentLength                             |
     +---------------+---------------+---------------+---------------+
    8| LUN or Reserved                                               |
     +                                                               +
   12|                                                               |
     +---------------+---------------+---------------+---------------+
   16| Initiator Task Tag or 0xffffffff                              |
     +---------------+---------------+---------------+---------------+
   20| Target Transfer Tag or SNACK Tag or 0xffffffff                |
     +---------------+---------------+---------------+---------------+
   24| Reserved                                                      |
     +---------------+---------------+---------------+---------------+
   28| ExpStatSN                                                     |
     +---------------+---------------+---------------+---------------+
   32/ Reserved                                                      /
    +/                                                               /
     +---------------+---------------+---------------+---------------+
   40| BegRun                                                        |
     +---------------------------------------------------------------+
   44| RunLength                                                     |
     +---------------------------------------------------------------+
   48| Header-Digest (Optional)                                      |
     +---------------+---------------+---------------+---------------+

ok so if the implementation supports ErrorRecoveryLevel greater than zero, it MUST support all SNACK types. No skipping, periodt ๐Ÿ’–.

The SNACK is how the initiator asks the target to resend numbered-responses, data, or R2T PDUs ๐Ÿ”. Each request describes "runs" ๐Ÿƒโ€โ™€๏ธ: the run starts at the first StatSN, DataSN, or R2TSN that needs resending, and the count says how many Status, Data, or R2T PDUs are wanted, the first one included. And 0 is special here ๐Ÿ‘€ (lowkey the most confusing part):

  • When it's used in RunLength, it means all PDUs starting with the initial.
  • When it's used in both BegRun and RunLength, it means all unacknowledged PDUs.

The numbered-response(s) or R2T(s) requested by a SNACK MUST be delivered as exact replicas of the ones the target sent originally, except for the fields ExpCmdSN, MaxCmdSN, and ExpDataSN, which MUST carry the current values. R2T(s) requested by SNACK MUST also carry the current value of StatSN ๐Ÿซถ.

The numbered Data-In PDUs requested by a Data SNACK MUST be delivered as exact replicas of the originals, except for the fields ExpCmdSN and MaxCmdSN, which MUST carry the current values, and except for resegmentation (see Section 10.16.3 Resegmentation) โœจ.

Any SNACK that requests a numbered-response, Data, or R2T that was not sent by the target, or was already acknowledged by the initiator, MUST be rejected with a reason code of "Protocol error". Do NOT be that person ๐Ÿ™…โ€โ™€๏ธ๐Ÿ’€.

10.16.1. Type ๐ŸŽ€

This field encodes the SNACK function like this:

  • 0 - Data/R2T SNACK: requesting retransmission of one or more Data-In or R2T PDUs.
  • 1 - Status SNACK: requesting retransmission of one or more numbered responses.
  • 2 - DataACK: positively acknowledges Data-In PDUs.
  • 3 - R-Data SNACK: requesting retransmission of Data-In PDUs with possible resegmentation and status tagging.

All other values are reserved ๐Ÿ”’.

A Data/R2T SNACK, Status SNACK, or R-Data SNACK for a command MUST precede status acknowledgement for the given command. The order matters, iykyk ๐Ÿ™ˆ.

10.16.2. Data Acknowledgement ๐ŸŒˆ

If an initiator operates at ErrorRecoveryLevel 1 or higher, it MUST issue a SNACK of type DataACK after receiving a Data-In PDU with the A bit set to 1. BUT, if the initiator has detected holes in the input sequence, it MUST postpone issuing the DataACK SNACK until those holes are filled ๐Ÿ•ณ๏ธ. An initiator MAY ignore the A bit if it decides the bit is being set aggressively by the target (for example, before the MaxBurstLength limit is reached) ๐Ÿ™ƒ.

The DataACK is used to free resources at the target. It is not used to request or imply data retransmission, so don't mix those up ๐Ÿ’….

An initiator MUST NOT request retransmission for any data it had already acknowledged. Once it's acked, it's acked, no cap ๐ŸงŠ.

10.16.3. Resegmentation ๐Ÿญ

If the initiator's MaxRecvDataSegmentLength changed between the original transmission and the time the initiator requests retransmission, the initiator MUST issue an R-Data SNACK (see Section 10.16.1 Type). With an R-Data SNACK, the initiator is saying it discards all the unacknowledged data and expects the target to resend it ๐Ÿ”„. It also expects resegmentation. In this case the retransmitted Data-In PDUs MAY be different from the ones originally sent, so they can reflect the changes in MaxRecvDataSegmentLength โœจ. Their DataSN starts with the BegRun of the last DataACK the target received, if it received any. Otherwise it starts with 0 and goes up by 1 for each resent Data-In PDU ๐Ÿ”ข.

A target that has received an R-Data SNACK MUST return a SCSI Response that contains a copy of the SNACK Tag field from the R-Data SNACK, in the SCSI Response SNACK Tag field, as its last or only Response ๐Ÿซถ. For example, if it already sent a response with a different value in the SNACK Tag field, or it had the status included in the last Data-In PDU, it must send a new SCSI Response PDU ๐Ÿ’Œ. If a target sends more than one SCSI Response PDU because of this rule, all SCSI responses must carry the same StatSN (see Section 10.4.4 SNACK Tag). And if an initiator tries to recover a lost SCSI Response (with a Status SNACK, see Section 10.16.1 Type) when more than one response has been sent, the target will send the SCSI Response with the latest content it knows about, including the last SNACK Tag for the command ๐Ÿง .

For considerations in allegiance reassignment of a task to a connection with a different MaxRecvDataSegmentLength, refer to Section 6.2.2 Allegiance Reassignment ๐Ÿฆ‹.

10.16.4. Initiator Task Tag ๐Ÿ’œ

For Status SNACK and DataACK, the Initiator Task Tag MUST be set to the reserved value 0xffffffff. In all other cases, the Initiator Task Tag field MUST be set to the Initiator Task Tag of the referenced command ๐ŸŽฏ.

10.16.5. Target Transfer Tag or SNACK Tag ๐ŸŒŸ

For an R-Data SNACK, this field MUST contain a value that is different from 0 or 0xffffffff, and it must be unique for the task (identified by the Initiator Task Tag). This value MUST be copied by the iSCSI target into the last or only SCSI Response PDU it issues for the command ๐ŸŽ€.

For DataACK, the Target Transfer Tag MUST contain a copy of the Target Transfer Tag and LUN provided with the SCSI Data-In PDU that has the A bit set to 1 ๐Ÿซง.

In all other cases, the Target Transfer Tag field MUST be set to the reserved value of 0xffffffff ๐Ÿ”’.

10.16.6. BegRun ๐Ÿ

This is the DataSN, R2TSN, or StatSN of the first PDU whose retransmission is requested (Data/R2T and Status SNACK), or the next expected DataSN (DataACK SNACK) ๐Ÿ‘€.

BegRun 0, used together with RunLength 0, means resend all unacknowledged Data-In, R2T, or Response PDUs ๐Ÿ’ซ.

BegRun MUST be 0 for an R-Data SNACK ๐Ÿ™…โ€โ™€๏ธ.

10.16.7. RunLength ๐Ÿ“

This is the number of PDUs whose retransmission is requested ๐Ÿ”ข.

RunLength 0 signals that all Data-In, R2T, or Response PDUs carrying numbers equal to or greater than BegRun have to be resent ๐Ÿ”.

The RunLength MUST also be 0 for a DataACK SNACK, in addition to an R-Data SNACK ๐ŸงŠ.

10.17. Reject โœจ

   Byte/     0       |       1       |       2       |       3       |
      /              |               |               |               |
     |0 1 2 3 4 5 6 7|0 1 2 3 4 5 6 7|0 1 2 3 4 5 6 7|0 1 2 3 4 5 6 7|
     +---------------+---------------+---------------+---------------+
    0|.|.| 0x3f      |1| Reserved    | Reason        | Reserved      |
     +---------------+---------------+---------------+---------------+
    4|TotalAHSLength | DataSegmentLength                             |
     +---------------+---------------+---------------+---------------+
    8/ Reserved                                                      /
    +/                                                               /
     +---------------+---------------+---------------+---------------+
   16| 0xffffffff                                                    |
     +---------------+---------------+---------------+---------------+
   20| Reserved                                                      |
     +---------------+---------------+---------------+---------------+
   24| StatSN                                                        |
     +---------------+---------------+---------------+---------------+
   28| ExpCmdSN                                                      |
     +---------------+---------------+---------------+---------------+
   32| MaxCmdSN                                                      |
     +---------------+---------------+---------------+---------------+
   36| DataSN/R2TSN or Reserved                                      |
     +---------------+---------------+---------------+---------------+
   40| Reserved                                                      |
     +---------------+---------------+---------------+---------------+
   44| Reserved                                                      |
     +---------------+---------------+---------------+---------------+
   48| Header-Digest (Optional)                                      |
     +---------------+---------------+---------------+---------------+
   xx/ Complete Header of Bad PDU                                    /
    +/                                                               /
     +---------------+---------------+---------------+---------------+
   yy/Vendor specific data (if any)                                  /
     /                                                               /
     +---------------+---------------+---------------+---------------+
   zz| Data-Digest (Optional)                                        |
     +---------------+---------------+---------------+---------------+

Reject is how an iSCSI error gets flagged ๐Ÿšจ. Think protocol mix-ups, unsupported options, that whole vibe. ๐Ÿ’…

10.17.1. Reason ๐ŸŽ€

The reject Reason is coded like this, and the table is honestly so organized ๐Ÿ˜ญ:

+------+----------------------------------------+------------------+
| Code | Explanation                            | Can the original |
| (hex)|                                        | PDU be re-sent?  |
+------+----------------------------------------+------------------+
| 0x01 | Reserved                               | no               |
|      |                                        |                  |
| 0x02 | Data (payload) Digest Error            | yes  (Note 1)    |
|      |                                        |                  |
| 0x03 | SNACK Reject                           | yes              |
|      |                                        |                  |
| 0x04 | Protocol Error (e.g., SNACK request for| no               |
|      | a status that was already acknowledged)|                  |
|      |                                        |                  |
| 0x05 | Command not supported                  | no               |
|      |                                        |                  |
| 0x06 | Immediate Command Reject - too many    | yes              |
|      | immediate commands                     |                  |
|      |                                        |                  |
| 0x07 | Task in progress                       | no               |
|      |                                        |                  |
| 0x08 | Invalid Data ACK                       | no               |
|      |                                        |                  |
| 0x09 | Invalid PDU field                      | no   (Note 2)    |
|      |                                        |                  |
| 0x0a | Long Operation Reject - Can't generate | yes              |
|      | Target Transfer Tag - out of resources |                  |
|      |                                        |                  |
| 0x0b | Negotiation Reset                      | no               |
|      |                                        |                  |
| 0x0c | Waiting for Logout                     | no               |
+------+----------------------------------------+------------------+

Note 1 (read this one, bestie ๐Ÿซถ): for iSCSI, a Data-Out PDU only gets retransmitted if the target asks for it with a recovery R2T. BUT if it's a data digest error on immediate data, the initiator may choose to resend the whole PDU, immediate data included.

Note 2: a target should use this reason code for all invalid values of PDU fields that are meant to describe a task, a response, or a data transfer. Some examples are invalid TTT/ITT, buffer offset, LUN qualifying a TTT, and an invalid sequence number in a SNACK. ๐Ÿ™ˆ

All other values for Reason are reserved, so those are off-limits ๐Ÿšซ.

In every case where a pre-instantiated SCSI task gets terminated because of the reject, the target MUST issue a proper SCSI command response with CHECK CONDITION, as described in Section 10.4.3 Response. If a status for the SCSI task was already sent before the reject, no additional status is required. ๐ŸŒธ If the error is detected while data from the initiator is still expected (that is, the command PDU did not contain all the data, and the target has not received a Data-Out PDU with the Final bit set to 1 for the unsolicited data, if any, and all outstanding R2Ts, if any), the target MUST wait until it receives the last expected Data-Out PDUs with the F bit set to 1 before sending the Response PDU. Patience is a whole aesthetic ๐Ÿงš.

For additional usage semantics of Reject PDU, see Section 6.3 Usage Of Reject PDU in Recovery. ๐Ÿฆ‹

10.17.2. DataSN/R2TSN ๐Ÿ“

This field is only valid if the rejected PDU is a Data/R2T SNACK and the Reject reason code is "Protocol error" (see Section 10.16 SNACK Request). The DataSN/R2TSN is the next Data/R2T sequence number that the target would send for the task, if any. โœจ

10.17.3. StatSN, ExpCmdSN and MaxCmdSN ๐Ÿ’œ

These fields carry their usual values and are not related to the rejected command. StatSN is advanced after a Reject. ๐ŸŒˆ

10.17.4. Complete Header of Bad PDU ๐Ÿ”ฅ

The target returns the header (not including digest) of the PDU in error as the data of the response. It's basically showing its work ๐Ÿ“.

10.18. NOP-Out ๐Ÿ“

   Byte/     0       |       1       |       2       |       3       |
      /              |               |               |               |
     |0 1 2 3 4 5 6 7|0 1 2 3 4 5 6 7|0 1 2 3 4 5 6 7|0 1 2 3 4 5 6 7|
     +---------------+---------------+---------------+---------------+
    0|.|I| 0x00      |1| Reserved                                    |
     +---------------+---------------+---------------+---------------+
    4|TotalAHSLength | DataSegmentLength                             |
     +---------------+---------------+---------------+---------------+
    8| LUN or Reserved                                               |
     +                                                               +
   12|                                                               |
     +---------------+---------------+---------------+---------------+
   16| Initiator Task Tag or 0xffffffff                              |
     +---------------+---------------+---------------+---------------+
   20| Target Transfer Tag or 0xffffffff                             |
     +---------------+---------------+---------------+---------------+
   24| CmdSN                                                         |
     +---------------+---------------+---------------+---------------+
   28| ExpStatSN                                                     |
     +---------------+---------------+---------------+---------------+
   32/ Reserved                                                      /
    +/                                                               /
     +---------------+---------------+---------------+---------------+
   48| Header-Digest (Optional)                                      |
     +---------------+---------------+---------------+---------------+
     / DataSegment - Ping Data (optional)                            /
    +/                                                               /
     +---------------+---------------+---------------+---------------+
     | Data-Digest (Optional)                                        |
     +---------------+---------------+---------------+---------------+

A NOP-Out is the initiator's ping ๐Ÿ“. It can be used to check that a connection/session is still active and all its components are operational, and the NOP-In response is the "ping echo" ๐Ÿ””.

The initiator also sends a NOP-Out in response to a NOP-In. ๐Ÿ’–

A NOP-Out may also be used to confirm a changed ExpStatSN if another PDU will not be available for a long time. โณ

Upon receipt of a NOP-In with the Target Transfer Tag set to a valid value (not the reserved 0xffffffff), the initiator MUST respond with a NOP-Out. In this case, the NOP-Out Target Transfer Tag MUST contain a copy of the NOP-In Target Transfer Tag. ๐Ÿซถ

10.18.1. Initiator Task Tag ๐ŸŒธ

The NOP-Out MUST have the Initiator Task Tag set to a valid value only if a response in the form of NOP-In is requested (i.e., the NOP-Out is used as a ping request). Otherwise, the Initiator Task Tag MUST be set to 0xffffffff. ๐ŸŽ€

When a target receives the NOP-Out with a valid Initiator Task Tag, it MUST respond with a Nop-In Response (see Section 10.19 NOP-In). ๐Ÿ’…

If the Initiator Task Tag contains 0xffffffff, the I bit MUST be set to 1, and the CmdSN is not advanced after this PDU is sent. ๐Ÿ‘‘

10.18.2. Target Transfer Tag ๐Ÿฆ‹

A target assigned identifier for the operation. ๐Ÿงš

The NOP-Out MUST only have the Target Transfer Tag set if it is issued in response to a NOP-In with a valid Target Transfer Tag. In this case, it copies the Target Transfer Tag from the NOP-In PDU. Otherwise, the Target Transfer Tag MUST be set to 0xffffffff. ๐Ÿ“

When the Target Transfer Tag is set to a value other than 0xffffffff, the LUN field MUST also be copied from the NOP-In. ๐ŸŒˆ

10.18.3. Ping Data ๐Ÿ”ฅ

Ping data are reflected in the NOP-In Response. The length of the reflected data is limited to MaxRecvDataSegmentLength. The length of ping data is indicated by the DataSegmentLength. 0 is a valid value for the DataSegmentLength and indicates the absence of ping data, so an empty ping is totally fine ๐Ÿ’œโœจ.

10.19. NOP-In ๐Ÿ’–

okay so NOP-In is the target's multitasking queen move ๐Ÿ‘‘ it's either a reply to a NOP-Out, a "ping" sent to the initiator ๐Ÿ“, or a way to sneak in an updated ExpCmdSN and/or MaxCmdSN when no other PDU is gonna show up for a long time (the target decides what "long" means ๐Ÿ•ฐ๏ธ)

   Byte/     0       |       1       |       2       |       3       |
      /             |               |               |               |
     |0 1 2 3 4 5 6 7|0 1 2 3 4 5 6 7|0 1 2 3 4 5 6 7|0 1 2 3 4 5 6 7|
     +---------------+---------------+---------------+---------------+
    0|.|.| 0x20      |1| Reserved                                    |
     +---------------+---------------+---------------+---------------+
    4|TotalAHSLength | DataSegmentLength                             |
     +---------------+---------------+---------------+---------------+
    8| LUN or Reserved                                               |
     +                                                               +
   12|                                                               |
     +---------------+---------------+---------------+---------------+
   16| Initiator Task Tag or 0xffffffff                              |
     +---------------+---------------+---------------+---------------+
   20| Target Transfer Tag or 0xffffffff                             |
     +---------------+---------------+---------------+---------------+
   24| StatSN                                                        |
     +---------------+---------------+---------------+---------------+
   28| ExpCmdSN                                                      |
     +---------------+---------------+---------------+---------------+
   32| MaxCmdSN                                                      |
     +---------------+---------------+---------------+---------------+
   36/ Reserved                                                      /
    +/                                                               /
     +---------------+---------------+---------------+---------------+
   48| Header-Digest (Optional)                                      |
     +---------------+---------------+---------------+---------------+
     / DataSegment - Return Ping Data                                /
    +/                                                               /
     +---------------+---------------+---------------+---------------+
     | Data-Digest (Optional)                                        |
     +---------------+---------------+---------------+---------------+

so when a target gets a NOP-Out with a valid Initiator Task Tag (aka not the reserved 0xffffffff), it MUST respond with a NOP-In carrying that same Initiator Task Tag from the NOP-Out request ๐Ÿซถ it MUST also copy back up to the first MaxRecvDataSegmentLength bytes of the Ping Data the initiator sent, and for these responses the Target Transfer Tag MUST be 0xffffffff ๐ŸŽ€

otherwise, when a target sends a NOP-In that is not a response to a Nop-Out from the initiator, the Initiator Task Tag MUST be set to 0xffffffff and the Data Segment MUST NOT contain any data (DataSegmentLength MUST be 0) ๐Ÿ™ˆ

10.19.1. Target Transfer Tag

  • if the target is responding to a NOP-Out, this is set to the reserved value 0xffffffff ๐Ÿ’…
  • if the target is sending a NOP-In as a Ping (intending to receive a corresponding NOP-Out), this field is set to a valid value (not the reserved 0xffffffff) ๐Ÿ“
  • if the target is initiating a NOP-In without wanting to receive a corresponding NOP-Out, this field MUST hold the reserved value of 0xffffffff ๐Ÿงš

10.19.2. StatSN

the StatSN field will always contain the next StatSN โœจ the one exception: when the Initiator Task Tag is set to 0xffffffff, the StatSN for the connection is not advanced after this PDU is sent ๐Ÿ’œ

10.19.3. LUN

a LUN MUST be set to a correct value when the Target Transfer Tag is valid (not the reserved value 0xffffffff) ๐Ÿ“

11. iSCSI Security Text Keys and Authentication Methods ๐Ÿ”

okay the security vibes start here ๐Ÿ”ฅ only the following keys are used during the SecurityNegotiation stage of the Login Phase:

  SessionType
  InitiatorName
  TargetName
  TargetAddress
  InitiatorAlias
  TargetAlias
  TargetPortalGroupTag
  AuthMethod and the keys used by the authentication methods
    specified under Section 11.1 AuthMethod along with all of
    their associated keys as well as Vendor Specific
    Authentication Methods.

other keys MUST NOT be used ๐Ÿ™…โ€โ™€๏ธ

SessionType, InitiatorName, TargetName, InitiatorAlias, TargetAlias, and TargetPortalGroupTag are described in Chapter 12 because they can also be used in the OperationalNegotiation stage ๐ŸŒˆ

all security keys have connection-wide applicability ๐Ÿฆ‹

11.1. AuthMethod

Use: During Login - Security Negotiation Senders: Initiator and
Target Scope: connection

AuthMethod = <list-of-values>

the main item of security negotiation is the authentication method (AuthMethod) ๐Ÿ’–

the authentication methods that can be used (the ones in the list-of-values) are either the ones in this table or vendor-unique methods:

+------------------------------------------------------------+
| Name          | Description                                |
+------------------------------------------------------------+
| KRB5          | Kerberos V5 - defined in [RFC1510]         |
+------------------------------------------------------------+
| SPKM1         | Simple Public-Key GSS-API Mechanism        |
|               | defined in [RFC2025]                       |
+------------------------------------------------------------+
| SPKM2         | Simple Public-Key GSS-API Mechanism        |
|               | defined in [RFC2025]                       |
+------------------------------------------------------------+
| SRP           | Secure Remote Password                     |
|               | defined in [RFC2945]                       |
+------------------------------------------------------------+
| CHAP          | Challenge Handshake Authentication Protocol|
|               | defined in [RFC1994]                       |
+------------------------------------------------------------+
| None          | No authentication                          |
+------------------------------------------------------------+

the AuthMethod selection is followed by an "authentication exchange" specific to whichever method got picked ๐ŸŽ€

the authentication method proposal can come from either the initiator or the target ๐Ÿซถ but the initiator MUST make the first step specific to the selected authentication method as soon as it's selected. so if the target makes the proposal, the initiator sends the first key(s) of the exchange together with its authentication method selection ๐Ÿ’…

the authentication exchange authenticates the initiator to the target, and optionally the target to the initiator โœจ authentication is OPTIONAL to use, but it MUST be supported by the target and initiator ๐Ÿ”ฅ

the initiator and target MUST implement CHAP ๐Ÿ‘‘ all other authentication methods are OPTIONAL ๐ŸŒธ

private or public extension algorithms MAY also be negotiated for authentication methods ๐Ÿงš whenever a private or public extension algorithm is part of the default offer (the offer made without any explicit administrative action), the implementer MUST ensure that CHAP is listed as an alternative in the default offer, and "None" is not part of the default offer ๐Ÿ™ˆ

extension authentication methods MUST be named using one of these two formats:

a)  Z-reversed.vendor.dns_name.do_something=
b)  Z<#><IANA-registered-string>=

methods named with the Z- format are private extensions ๐Ÿ”’ methods named with the Z# format are public extensions that must be registered with IANA and MUST be described by an informational RFC ๐Ÿ“š

for all of the public or private extension authentication methods, the method-specific keys MUST conform to the format specified in Section 5.1 Text Format for standard-label ๐Ÿ“

to identify the vendor for private extension authentication methods, we suggest using the reversed DNS-name as a prefix to the proper digest names ๐ŸŒˆ

the part of digest-name following Z- and Z# MUST conform to the format for standard-label specified in Section 5.1 Text Format ๐Ÿ’–

support for public or private extension authentication methods is OPTIONAL ๐Ÿฆ‹

the following subsections define the specific exchanges for each of the standardized authentication methods. like I said earlier, the first step is always done by the initiator ๐Ÿซถ

11.1.1. Kerberos

for KRB5 (Kerberos V5) [RFC1510] and [RFC1964], the initiator MUST use:

KRB_AP_REQ=<KRB_AP_REQ>

where KRB_AP_REQ is the client message as defined in [RFC1510] ๐Ÿ”

the default principal name assumed by an iSCSI initiator or target (before any administrative configuration action) MUST be the iSCSI Initiator Name or iSCSI Target Name respectively, prefixed by the string "iscsi/" ๐ŸŽ€

if the initiator authentication fails, the target MUST respond with a Login reject with "Authentication Failure" status. otherwise, if the initiator has selected the mutual authentication option (by setting MUTUAL-REQUIRED in the ap-options field of the KRB_AP_REQ), the target MUST reply with:

KRB_AP_REP=<KRB_AP_REP>

where KRB_AP_REP is the server's response message as defined in [RFC1510] โœจ

if mutual authentication was selected and target authentication fails, the initiator MUST close the connection ๐Ÿ’€

KRB_AP_REQ and KRB_AP_REP are binary-values, and their binary length (not the length of the character string that represents them in encoded form) MUST not exceed 65536 bytes ๐ŸŒŸ

11.1.2. Simple Public-Key Mechanism (SPKM)

for SPKM1 and SPKM2 [RFC2025], the initiator MUST use:

SPKM_REQ=<SPKM-REQ>

where SPKM-REQ is the first initiator token as defined in [RFC2025] ๐Ÿงš

[RFC2025] defines situations where each side may send an error token that may cause the peer to re-generate and resend its last token. this scheme is followed in iSCSI, and the error token syntax is:

SPKM_ERROR=<SPKM-ERROR>

however, the SPKM-DEL tokens that [RFC2025] defines for fatal errors will not be used by iSCSI ๐Ÿ™…โ€โ™€๏ธ if the target needs to send a SPKM-DEL token, it will instead send a Login "login reject" message with the "Authentication Failure" status and terminate the connection. if the initiator needs to send a SPKM-DEL token, it will close the connection ๐Ÿ’”

in the following sections, we assume that no SPKM-ERROR tokens are required ๐ŸŒธ

if the initiator authentication fails, the target MUST return an error. otherwise, if the AuthMethod is SPKM1, or if the initiator has selected the mutual authentication option (by setting the mutual-state bit in the options field of the REQ-TOKEN in the SPKM-REQ), the target MUST reply with:

SPKM_REP_TI=<SPKM-REP-TI>

where SPKM-REP-TI is the target token as defined in [RFC2025] ๐Ÿฆ‹

if mutual authentication was selected and target authentication fails, the initiator MUST close the connection. otherwise, if the AuthMethod is SPKM1, the initiator MUST continue with:

SPKM_REP_IT=<SPKM-REP-IT>

where SPKM-REP-IT is the second initiator token as defined in [RFC2025]. if the initiator authentication fails, the target MUST answer with a Login reject with "Authentication Failure" status ๐Ÿ˜ญ

SPKM requires support for very long authentication items ๐Ÿ“

all the SPKM-* tokens are binary-values, and their binary length (not the length of the character string that represents them in encoded form) MUST not exceed 65536 bytes ๐Ÿ’œ

11.1.3. Secure Remote Password (SRP) ๐Ÿ”โœจ

okay so SRP time bestie ๐Ÿ’… For SRP [RFC2945], the initiator MUST use:

SRP_U=<U> TargetAuth=Yes   /* or TargetAuth=No */

The target MUST answer with a Login reject with the "Authorization Failure" status or reply with:

SRP_GROUP=<G1,G2...> SRP_s=<s>

Where G1,G2... are proposed groups, in order of preference.

The initiator MUST either close the connection or continue with:

SRP_A=<A> SRP_GROUP=<G>

Where G is one of G1,G2... that were proposed by the target.

The target MUST answer with a Login reject with the "Authentication Failure" status or reply with:

SRP_B=<B>

The initiator MUST close the connection or continue with:

SRP_M=<M>

If the initiator authentication fails, the target MUST answer with a Login reject with "Authentication Failure" status. Otherwise, if the initiator sent TargetAuth=Yes in the first message (requiring target authentication), the target MUST reply with:

SRP_HM=<H(A | M | K)>

If the target authentication fails, the initiator MUST close the connection.

Where U, s, A, B, M, and H(A | M | K) are defined in [RFC2945] (using the SHA1 hash function, such as SRP-SHA1) and G,Gn (Gn stands for G1,G2...) are identifiers of SRP groups specified in [RFC3723]. G, Gn, and U are text strings, s,A,B,M, and H(A | M | K) are binary-values. The length of s,A,B,M and H(A | M | K) in binary form (not the length of the character string that represents them in encoded form) MUST not exceed 1024 bytes.

For the SRP_GROUP, all the groups specified in [RFC3723] up to 1536 bits (i.e., SRP-768, SRP-1024, SRP-1280, SRP-1536) must be supported by initiators and targets. To guarantee interoperability, targets MUST always offer "SRP-1536" as one of the proposed groups. ๐Ÿซถ

11.1.4. Challenge Handshake Authentication Protocol (CHAP) ๐Ÿงฉ

For CHAP [RFC1994], in the first step, the initiator MUST send:

CHAP_A=<A1,A2...>

Where A1,A2... are proposed algorithms, in order of preference.

In the second step, the target MUST answer with a Login reject with the "Authentication Failure" status or reply with:

CHAP_A=<A> CHAP_I=<I> CHAP_C=<C>

Where A is one of A1,A2... that were proposed by the initiator.

In the third step, the initiator MUST continue with:

CHAP_N=<N> CHAP_R=<R>

or, if it requires target authentication, with:

CHAP_N=<N> CHAP_R=<R> CHAP_I=<I> CHAP_C=<C>

If the initiator authentication fails, the target MUST answer with a Login reject with "Authentication Failure" status. Otherwise, if the initiator required target authentication, the target MUST either answer with a Login reject with "Authentication Failure" or reply with:

CHAP_N=<N> CHAP_R=<R>

If target authentication fails, the initiator MUST close the connection.

Where N, (A,A1,A2), I, C, and R are (correspondingly) the Name, Algorithm, Identifier, Challenge, and Response as defined in [RFC1994], N is a text string, A,A1,A2, and I are numbers, and C and R are large-binary-values and their binary length (not the length of the character string that represents them in encoded form) MUST not exceed 1024 bytes.

For the Algorithm, as stated in [RFC1994], one value is required to be implemented:

5     (CHAP with MD5)

To guarantee interoperability, initiators MUST always offer it as one of the proposed algorithms. ๐Ÿ’œ


12. Login/Text Operational Text Keys ๐ŸŽ€

okay this part is about the operational keys, and some of them are super strict ๐Ÿ˜ญ Some session specific parameters MUST only be carried on the leading connection and cannot be changed after the leading connection login (e.g., MaxConnections, the maximum number of connections). This holds for a single connection session with regard to connection restart. The keys that fall into this category have the use: LO (Leading Only).

Keys that can only be used during login have the use: IO (initialize only), while those that can be used in both the Login Phase and Full Feature Phase have the use: ALL.

Keys that can only be used during Full Feature Phase use FFPO (Full Feature Phase only).

Keys marked as Any-Stage may also appear in the SecurityNegotiation stage while all other keys described in this chapter are operational keys.

Keys that do not require an answer are marked as Declarative.

Key scope is indicated as session-wide (SW) or connection-only (CO).

Result function, wherever mentioned, states the function that can be applied to check the validity of the responder selection. Minimum means that the selected value cannot exceed the offered value. Maximum means that the selected value cannot be lower than the offered value. AND means that the selected value must be a possible result of a Boolean "and" function with an arbitrary Boolean value (e.g., if the offered value is No the selected value must be No). OR means that the selected value must be a possible result of a Boolean "or" function with an arbitrary Boolean value (e.g., if the offered value is Yes the selected value must be Yes).

12.1. HeaderDigest and DataDigest ๐Ÿงฎ

Use: IO
Senders: Initiator and Target
Scope: CO

HeaderDigest = <list-of-values>
DataDigest = <list-of-values>

Default is None for both HeaderDigest and DataDigest.

Digests enable the checking of end-to-end, non-cryptographic data integrity beyond the integrity checks provided by the link layers and the covering of the whole communication path including all elements that may change the network level PDUs such as routers, switches, and proxies.

The following table lists cyclic integrity checksums that can be negotiated for the digests and that MUST be implemented by every iSCSI initiator and target. These digest options only have error detection significance.

+---------------------------------------------+
| Name          | Description     | Generator |
+---------------------------------------------+
| CRC32C        | 32 bit CRC      |0x11edc6f41|
+---------------------------------------------+
| None          | no digest                   |
+---------------------------------------------+

The generator polynomial for this digest is given in hex-notation (e.g., 0x3b stands for 0011 1011 and the polynomial is x5+X4+x**3+x+1).

When the Initiator and Target agree on a digest, this digest MUST be used for every PDU in Full Feature Phase.

Padding bytes, when present in a segment covered by a CRC, SHOULD be set to 0 and are included in the CRC.

The CRC MUST be calculated by a method that produces the same results as the following process:

  • The PDU bits are considered as the coefficients of a polynomial M(x) of degree n-1; bit 7 of the lowest numbered byte is considered the most significant bit (x^n-1), followed by bit 6 of the lowest numbered byte through bit 0 of the highest numbered byte (x^0).
  • The most significant 32 bits are complemented.
  • The polynomial is multiplied by x^32 then divided by G(x). The generator polynomial produces a remainder R(x) of degree <= 31.
  • The coefficients of R(x) are considered a 32 bit sequence.
  • The bit sequence is complemented and the result is the CRC.
  • The CRC bits are mapped into the digest word. The x^31 coefficient in bit 7 of the lowest numbered byte of the digest continuing through to the byte up to the x^24 coefficient in bit 0 of the lowest numbered byte, continuing with the x^23 coefficient in bit 7 of next byte through x^0 in bit 0 of the highest numbered byte.
  • Computing the CRC over any segment (data or header) extended to include the CRC built using the generator 0x11edc6f41 will always get the value 0x1c2d19ed as its final remainder (R(x)). This value is given here in its polynomial form (i.e., not mapped as the digest word).

For a discussion about selection criteria for the CRC, see [RFC3385]. For a detailed analysis of the iSCSI polynomial, see [Castagnoli93].

Private or public extension algorithms MAY also be negotiated for digests. Whenever a private or public digest extension algorithm is part of the default offer (the offer made in absence of explicit administrative action) the implementer MUST ensure that CRC32C is listed as an alternative in the default offer and "None" is not part of the default offer.

Extension digest algorithms MUST be named using one of the following two formats:

a) Y-reversed.vendor.dns_name.do_something=
b) Y<#><IANA-registered-string>=

Digests named using the Y- format are used for private purposes (unregistered). Digests named using the Y# format (public extension) must be registered with IANA and MUST be described by an informational RFC.

For private extension digests, to identify the vendor, we suggest you use the reversed DNS-name as a prefix to the proper digest names.

The part of digest-name following Y- and Y# MUST conform to the format for standard-label specified in Section 5.1 Text Format.

Support for public or private extension digests is OPTIONAL.

12.2. MaxConnections ๐Ÿ”ข

Use: LO
Senders: Initiator and Target
Scope: SW
Irrelevant when: SessionType=Discovery

MaxConnections=<numerical-value-from-1-to-65535>

Default is 1.
Result function is Minimum.

Initiator and target negotiate the maximum number of connections requested/acceptable. ๐Ÿฆ‹

12.3. SendTargets ๐Ÿ“ฌ

Use: FFPO
Senders: Initiator
Scope: SW

For a complete description, see Appendix D. - SendTargets Operation -.

12.4. TargetName ๐ŸŽฏ

Use: IO by initiator, FFPO by target - only as response to a
     SendTargets, Declarative, Any-Stage

Senders: Initiator and Target
Scope: SW

TargetName=<iSCSI-name-value>

Examples:

   TargetName=iqn.1993-11.com.disk-vendor:diskarrays.sn.45678
   TargetName=eui.020000023B040506

The initiator of the TCP connection MUST provide this key to the remote endpoint in the first login request if the initiator is not establishing a discovery session. The iSCSI Target Name specifies the worldwide unique name of the target.

The TargetName key may also be returned by the "SendTargets" text request (which is its only use when issued by a target).

TargetName MUST not be redeclared within the login phase. ๐Ÿšซ

12.5. InitiatorName ๐Ÿชช

Use: IO, Declarative, Any-Stage
Senders: Initiator
Scope: SW

InitiatorName=<iSCSI-name-value>

Examples:

   InitiatorName=iqn.1992-04.com.os-vendor.plan9:cdrom.12345
   InitiatorName=iqn.2001-02.com.ssp.users:customer235.host90

The initiator of the TCP connection MUST provide this key to the remote endpoint at the first Login of the Login Phase for every connection. The InitiatorName key enables the initiator to identify itself to the remote endpoint.

InitiatorName MUST not be redeclared within the login phase. ๐Ÿ™ˆ

12.6. TargetAlias ๐ŸŒธ

Use: ALL, Declarative, Any-Stage
Senders: Target
Scope: SW

TargetAlias=<iSCSI-local-name-value>

Examples:

   TargetAlias=Bob-s Disk
   TargetAlias=Database Server 1 Log Disk
   TargetAlias=Web Server 3 Disk 20

If a target has been configured with a human-readable name or description, this name SHOULD be communicated to the initiator during a Login Response PDU if SessionType=Normal (see Section 12.21 SessionType). This string is not used as an identifier, nor is it meant to be used for authentication or authorization decisions. It can be displayed by the initiator's user interface in a list of targets to which it is connected. โœจ

12.7. InitiatorAlias ๐Ÿ’–

Use: ALL, Declarative, Any-Stage
Senders: Initiator
Scope: SW
InitiatorAlias=<iSCSI-local-name-value>

Examples:

InitiatorAlias=Web Server 4
InitiatorAlias=spyalley.nsa.gov
InitiatorAlias=Exchange Server

okay so if an initiator has a human-readable name or description, it SHOULD be shared with the target during a Login Request PDU ๐Ÿซถ And if it doesn't have one? The host name can be used instead ๐Ÿ“

This string is not an identifier, and it's not for authentication or authorization decisions, so don't use it for that ๐Ÿ™…โ€โ™€๏ธ The target's user interface can display it in a list of the initiators that are connected to it โœจ

12.8. TargetAddress ๐Ÿฆ‹

Use: ALL, Declarative, Any-Stage
Senders: Target
Scope: SW
TargetAddress=domainname[:port][,portal-group-tag]

The domainname can be a DNS host name, a dotted-decimal IPv4 address, or a bracketed IPv6 address, as described in [RFC2732] ๐ŸŒˆ

If you don't specify the TCP port, it's assumed to be the IANA-assigned default port for iSCSI (see Section 13 IANA Considerations) ๐Ÿ’…

If the TargetAddress comes back as the result of a redirect status in a login response, the comma and portal group tag MUST be omitted ๐Ÿ™ˆ

If the TargetAddress comes back inside a SendTargets response, the portal group tag MUST be included ๐Ÿซถ

Examples:

TargetAddress=10.0.0.1:5003,1
TargetAddress=[1080:0:0:0:8:800:200C:417A],65
TargetAddress=[1080::8:800:200C:417A]:5003,1
TargetAddress=computingcenter.example.com,23

Use of the portal-group-tag is described in Appendix D ๐Ÿงš

- SendTargets Operation -. The formats for the port and portal-group-tag are the same as the one specified in Section 12.9 TargetPortalGroupTag ๐Ÿ’œ

12.9. TargetPortalGroupTag ๐ŸŒธ

Use: IO by target, Declarative, Any-Stage
Senders: Target
Scope: SW
TargetPortalGroupTag=<16-bit-binary-value>

Example:

TargetPortalGroupTag=1

okay so the target portal group tag is a 16-bit binary value that uniquely identifies a portal group within an iSCSI target node ๐Ÿ‘‘ It carries the tag of the portal group that's servicing the Login request. The iSCSI target sends this key back to the initiator in the Login Response PDU to the first Login Request PDU that has the C bit set to 0, when the initiator gives a TargetName ๐Ÿ”ฅ

For the complete usage expectations of this key, see Section 5.3 Login Phase โœจ

12.10. InitialR2T ๐ŸŽ€

Use: LO
Senders: Initiator and Target
Scope: SW
Irrelevant when: SessionType=Discovery
InitialR2T=<boolean-value>

Examples:

I->InitialR2T=No
T->InitialR2T=No

Default is Yes. Result function is OR.

The InitialR2T key is how you turn off the default use of R2T for unidirectional commands and the output part of bidirectional commands ๐Ÿง That lets an initiator start sending data right away, as if it had already received an initial R2T with Buffer Offset=Immediate Data Length and Desired Data Transfer Length=(min(FirstBurstLength, Expected Data Transfer Length) - Received Immediate Data Length). It's giving "I already got permission" energy ๐Ÿ˜Œ

By default, R2T is required, unless both the initiator and the target send this key-pair attribute with InitialR2T=No. Only the first outgoing data burst (immediate data and/or separate PDUs) can go out unsolicited, meaning without an explicit R2T ๐Ÿ’–

12.11. ImmediateData ๐Ÿฆ‹

Use: LO
Senders: Initiator and Target
Scope: SW
Irrelevant when: SessionType=Discovery
ImmediateData=<boolean-value>

Default is Yes. Result function is AND.

The initiator and target negotiate support for immediate data ๐Ÿค To turn it off, the initiator or target has to say so. ImmediateData can only be turned on if both the initiator and the target have ImmediateData=Yes ๐ŸŒˆ

If ImmediateData is Yes and InitialR2T is Yes (the default), then only immediate data is accepted in the first burst ๐Ÿ™Œ

If ImmediateData is No and InitialR2T is Yes, the initiator MUST NOT send unsolicited data, and the target MUST reject unsolicited data with the corresponding response code ๐Ÿšซ

If ImmediateData is No and InitialR2T is No, the initiator MUST NOT send unsolicited immediate data, but it MAY send one unsolicited burst of Data-Out PDUs ๐Ÿ“

If ImmediateData is Yes and InitialR2T is No, the initiator MAY send unsolicited immediate data and/or one unsolicited burst of Data-Out PDUs โœจ

Here's a quick summary table of the unsolicited data options ๐Ÿ’…

+----------+-------------+------------------+--------------+
|InitialR2T|ImmediateData|    Unsolicited   |Immediate Data|
|          |             |   Data Out PDUs  |              |
+----------+-------------+------------------+--------------+
| No       | No          | Yes              | No           |
+----------+-------------+------------------+--------------+
| No       | Yes         | Yes              | Yes          |
+----------+-------------+------------------+--------------+
| Yes      | No          | No               | No           |
+----------+-------------+------------------+--------------+
| Yes      | Yes         | No               | Yes          |
+----------+-------------+------------------+--------------+

12.12. MaxRecvDataSegmentLength ๐ŸŒธ

Use: ALL, Declarative
Senders: Initiator and Target
Scope: CO
MaxRecvDataSegmentLength=<numerical-value-512-to-(2**24-1)>

Default is 8192 bytes.

Each initiator or target declares the maximum data segment length, in bytes, that it can receive in an iSCSI PDU ๐Ÿ“

The transmitter (initiator or target) is required to send PDUs whose data segment doesn't exceed the receiver's MaxRecvDataSegmentLength ๐Ÿ’œ

A target receiver is also limited by MaxBurstLength for solicited data and by FirstBurstLength for unsolicited data. An initiator MUST NOT send solicited PDUs that exceed MaxBurstLength, nor unsolicited PDUs that exceed FirstBurstLength (or FirstBurstLength-Immediate Data Length if immediate data was sent) ๐Ÿšซโœจ

12.13. MaxBurstLength ๐Ÿงš

Use: LO
Senders: Initiator and Target
Scope: SW
Irrelevant when: SessionType=Discovery
MaxBurstLength=<numerical-value-512-to-(2**24-1)>
Default is 262144 (256 Kbytes).
Result function is Minimum.

The initiator and target negotiate the maximum SCSI data payload, in bytes, for a Data-In or solicited Data-Out iSCSI sequence ๐Ÿ“ฆ A sequence is one or more consecutive Data-In or Data-Out PDUs that ends with a Data-In or Data-Out PDU whose F bit is set to one ๐Ÿฆ‹

12.14. FirstBurstLength ๐ŸŽ€

Use: LO
Senders: Initiator and Target
Scope: SW
Irrelevant when: SessionType=Discovery
Irrelevant when: ( InitialR2T=Yes and ImmediateData=No )
FirstBurstLength=<numerical-value-512-to-(2**24-1)>
Default is 65536 (64 Kbytes).
Result function is Minimum.

The initiator and target negotiate the maximum amount of unsolicited data, in bytes, that an iSCSI initiator can send to the target while a single SCSI command is running ๐Ÿ’– That covers the immediate data (if any) and the sequence of unsolicited Data-Out PDUs (if any) that follow the command.

FirstBurstLength MUST NOT exceed MaxBurstLength ๐Ÿ™…โ€โ™€๏ธ

12.15. DefaultTime2Wait โณ

Use: LO
Senders: Initiator and Target
Scope: SW
DefaultTime2Wait=<numerical-value-0-to-3600>
Default is 2.
Result function is Maximum.

The initiator and target negotiate the minimum time, in seconds, to wait before trying an explicit or implicit logout, or an active task reassignment, after an unexpected connection termination or a connection reset ๐ŸŒ™

A value of 0 means logout or active task reassignment can be tried immediately โšก

12.16. DefaultTime2Retain ๐ŸŒˆ

Use: LO Senders: Initiator and Target Scope: SW
DefaultTime2Retain=<numerical-value-0-to-3600>

Default is 20.  Result function is Minimum.

The initiator and target negotiate the maximum time, in seconds after the initial wait (Time2Wait), during which an active task reassignment is still possible after an unexpected connection termination or a connection reset ๐Ÿ•ฐ๏ธ

This value is also the session state timeout if the connection in question is the last LOGGED_IN connection in the session ๐Ÿซถ

A value of 0 means the target discards the connection and task state immediately ๐Ÿ’€

12.17. MaxOutstandingR2T ๐Ÿ‘‘

Use: LO
Senders: Initiator and Target
Scope: SW
MaxOutstandingR2T=<numerical-value-from-1-to-65535>
Irrelevant when: SessionType=Discovery

Default is 1.
Result function is Minimum.

The initiator and target negotiate the maximum number of outstanding R2Ts per task, not counting any implied initial R2T that might be part of that task ๐Ÿ”ฅ An R2T stays outstanding until the last data PDU (with the F bit set to 1) is transferred, or until a sequence reception timeout (Section 6.1.4.1 Recovery Within-command) is hit for that data sequence โฑ๏ธ

12.18. DataPDUInOrder ๐Ÿ“

Use: LO
Senders: Initiator and Target
Scope: SW
Irrelevant when: SessionType=Discovery
DataPDUInOrder=<boolean-value>
Default is Yes.
Result function is OR.

No tells iSCSI that the data PDUs within a sequence can arrive in any order ๐ŸŽฒ Yes means the data PDUs within a sequence have to sit at continuously increasing addresses, and overlaps are forbidden ๐Ÿ“

12.19. DataSequenceInOrder ๐Ÿงฉ

Use: LO
Senders: Initiator and Target
Scope: SW
Irrelevant when: SessionType=Discovery
DataSequenceInOrder=<boolean-value>

Default is Yes. Result function is OR.

A Data Sequence is a sequence of Data-In or Data-Out PDUs that ends with a Data-In or Data-Out PDU whose F bit is set to one ๐Ÿฆ‹ A Data-Out sequence is sent either unsolicited or in response to an R2T. Sequences cover an offset range ๐Ÿ“

If DataSequenceInOrder is No, Data PDU sequences can be transferred in any order ๐ŸŒ€

If DataSequenceInOrder is Yes, Data Sequences MUST be transferred using continuously non-decreasing sequence offsets (the R2T buffer offset for writes, or the smallest SCSI Data-In buffer offset within a read data sequence) ๐Ÿ“ˆ

If DataSequenceInOrder is Yes, a target may retry at most the last R2T, and an initiator may at most request retransmission for the last read data sequence. For this reason, if ErrorRecoveryLevel is not 0 and DataSequenceInOrder is Yes, then MaxOustandingR2T MUST be set to 1 ๐ŸŒŸ

Heads up bestie: when ErrorRecoveryLevel is not 0 and DataSequenceInOrder is Yes, MaxOustandingR2T has to be 1. No exceptions ๐Ÿ™ˆ

12.20. ErrorRecoveryLevel ๐Ÿ’œ

Use: LO
Senders: Initiator and Target
Scope: SW
ErrorRecoveryLevel=<numerical-value-0-to-2>
Default is 0.
Result function is Minimum.

The initiator and target negotiate the recovery level they both support ๐Ÿ›Ÿ

Recovery levels are combinations of recovery capabilities. Each level includes all the capabilities of the lower levels and adds some new ones on top ๐Ÿชœ

When the recovery mechanisms are described, certain recovery classes are specified. Section 6.1.5 Error Recovery Hierarchy explains how those classes map to the levels ๐Ÿ—บ๏ธ

12.21. SessionType โœจ

Use: LO, Declarative, Any-Stage
Senders: Initiator
Scope: SW
SessionType= <Discovery|Normal>

Default is Normal ๐Ÿ’–

The initiator picks what kind of session it wants to make ๐ŸŽ€ and the target can either say yes or say no ๐Ÿ™…โ€โ™€๏ธ

A discovery session tells the Target that its ONLY job is discovery ๐Ÿ” The only requests a target accepts in this kind of session are a text request with a SendTargets key and a logout request with reason "close the session" ๐Ÿงš

Also, a discovery session implies MaxConnections = 1 and overrides both the default and any explicit setting. Lowkey iconic that it wins over everything ๐Ÿ‘‘

12.22. The Private or Public Extension Key Format

Use: ALL
Senders: Initiator and Target
Scope: specific key dependent
X-reversed.vendor.dns_name.do_something=

or

X<#><IANA-registered-string>=

Okay so these keys are for public or private extension stuff ๐Ÿฆ‹ They always start with X- if they're unregistered with IANA (private) or X# if they're registered with IANA (public) ๐Ÿ’œ

For unregistered keys, the vendor gets identified by putting the reversed DNS-name in front of the key-proper. We suggest it, so it's lowkey the move ๐Ÿ“

The part of the key-name that comes after X- and X# MUST conform to the format for key-name specified in Section 5.1 Text Format. No cap, that's the rule ๐Ÿ“

For IANA registered keys, the string after X# must be registered with IANA AND the use of the key MUST be described by an informational RFC ๐Ÿ“œ

Vendor specific keys MUST ONLY be used in normal sessions ๐Ÿšซ Not discovery, bestie!

Support for public or private extension keys is OPTIONAL ๐ŸŒธ

13. IANA Considerations โœจ

This section conforms to [RFC2434] ๐ŸŽ€

The well-known user TCP port number for iSCSI connections assigned by IANA is 3260, and this is the default iSCSI port ๐Ÿ”Œ Implementations that need a system TCP port number may use port 860, which is the port IANA assigned as the iSCSI system port. However, to use port 860 it MUST be explicitly specified. Implementations MUST NOT default to port 860, because 3260 is the only allowed default ๐Ÿ™ˆ

Extension keys, authentication methods, or digest types that a vendor (or a group of vendors) plans to make publicly available descriptions for MUST be described by an RFC and MUST be registered with IANA ๐Ÿ’…

The IANA has set up the following three registries ๐Ÿ—‚๏ธ

  • a) iSCSI extended key registry
  • b) iSCSI authentication methods registry
  • c) iSCSI digests registry

[RFC3723] also tells IANA to keep a registry for the values of the SRP_GROUP key. The format of these values must conform to the one specified for iSCSI extension item-label in Section 13.5.4 Standard iSCSI extension item-label format ๐Ÿง

For the iSCSI authentication methods registry and the iSCSI digests registry, IANA MUST also assign a 16-bit unsigned integer number (the method number for the authentication method and the digest number for the digest) ๐Ÿ”ข

The following initial values for the registry for authentication methods are specified by the standards action of this document โœจ

 Authentication Method                   | Number |
+----------------------------------------+--------+
| CHAP                                   |     1  |
+----------------------------------------+--------+
| SRP                                    |     2  |
+----------------------------------------+--------+
| KRB5                                   |     3  |
+----------------------------------------+--------+
| SPKM1                                  |     4  |
+----------------------------------------+--------+
| SPKM2                                  |     5  |
+----------------------------------------+--------+

All other record numbers from 0 to 255 are reserved. IANA will register numbers above 255 ๐ŸŒˆ

Authentication methods with numbers above 255 MUST be unique within the registry and MUST be used with the prefix Z# ๐Ÿ”

The following initial values for the registry for digests are specified by the standards action of this document ๐Ÿฆ‹

Digest                                  | Number |
+----------------------------------------+--------+
| CRC32C                                 |     1  |
+----------------------------------------+--------+

All other record numbers from 0 to 255 are reserved. IANA will register numbers above 255 โญ

Digests with numbers above 255 MUST be unique within the registry and MUST be used with the prefix Y# ๐Ÿ’œ

The RFC that describes the item to be registered MUST indicate in the IANA Considerations section the string and iSCSI registry it should be recorded in ๐Ÿ“

Extension Keys, Authentication Methods, and digests (iSCSI extension items) must conform to a number of requirements, as described below. It's giving checklist ๐Ÿ’…

13.1. Naming Requirements

Each iSCSI extension item must have a unique name in its category. This name will be used as a standard-label for the key, access method, or digest, and it must conform to the syntax in Section 13.5.4 Standard iSCSI extension item-label format for iSCSI extension item-labels ๐Ÿท๏ธ

13.2. Mechanism Specification Requirements

For iSCSI extension items, all of the protocols and procedures a given iSCSI extension item uses must be described. That can happen in the item's own specification or in some other publicly available specification ๐Ÿ“– The description has to be detailed enough for any competent implementor to build it ๐Ÿ› ๏ธ Use of secret and/or proprietary methods in iSCSI extension items is expressly prohibited ๐Ÿ™…โ€โ™€๏ธ On top of that, the restrictions that [RFC1602] puts on standardizing patented algorithms must be respected โš–๏ธ

13.3. Publication Requirements

All iSCSI extension items must be described by an RFC. The RFC may be informational rather than Standards-Track, although Standards Track review and approval are encouraged for all iSCSI extension items ๐Ÿ“š

13.4. Security Requirements

Any known security issues that come from using the iSCSI extension item must be completely and fully described ๐Ÿšจ It's not required that the iSCSI extension item be secure or free from risks. What matters is that the known risks get identified ๐Ÿซถ Publishing a new iSCSI extension item does not require an exhaustive security review, and the security considerations section is subject to continuing evaluation ๐Ÿ”

Additional security considerations should be addressed by publishing revised versions of the iSCSI extension item specification ๐Ÿ”„

For each of these registries, IANA must record the registered string, which MUST conform to the format rules in Section 13.5.4 Standard iSCSI extension item-label format for iSCSI extension item-labels, along with the RFC number that describes it. The key prefix (X#, Y# or Z#) is not part of the recorded string ๐Ÿ’€

13.5. Registration Procedure

Registration of a new iSCSI extension item starts with building an Internet Draft to become an RFC โœ๏ธ

13.5.1. Present the iSCSI extension item to the Community

Send a proposed access type specification to the IPS WG mailing list. If the IPS WG has been disbanded at registration time, send it to a mailing list the IETF Transport Area Director designates instead, for a review period of a month ๐Ÿ“ฃ The whole point of the public posting is to collect comments and feedback on the iSCSI extension item specification and to review any security considerations ๐ŸŽ€

13.5.2. iSCSI extension item review and IESG approval

When the one-month period has passed, the IPS WG chair (or a person the IETF Transport Area Director nominates, the iSCSI extension item reviewer) forwards the Internet Draft to the IESG for publication as an informational RFC, or rejects it ๐Ÿง If the specification is a standards track document, the usual IETF procedures for those get followed โœจ

Decisions made by the iSCSI extension item reviewer must be published within two weeks after the month-long review period. Decisions made by the iSCSI extension item reviewer can be appealed through the IESG appeal process โš–๏ธ

13.5.3. IANA Registration

As long as the iSCSI extension item has either passed review or been successfully appealed to the IESG, and the specification is published as an RFC, IANA will register the iSCSI extension item and make the registration available to the community ๐Ÿฅณ

13.5.4. Standard iSCSI extension item-label format

The following character symbols are used in iSCSI extension item-labels. The hexadecimal values represent Unicode code points ๐Ÿ”ค

(a-z, A-Z) - letters
(0-9) - digits
"."  (0x2e) - dot
"-"  (0x2d) - minus
"+"  (0x2b) - plus
"@"  (0x40) - commercial at
"_"  (0x5f) - underscore

An iSCSI extension item-label is a string of one or more characters made up of letters, digits, dot, minus, plus, commercial at, or underscore. An iSCSI extension item-label MUST begin with a capital letter and must not exceed 63 characters ๐ŸŒŸ

13.6. IANA Procedures for Registering iSCSI extension items

The identity of the iSCSI extension item reviewer is communicated to IANA by the IESG. After that, IANA only acts on iSCSI extension item definitions that the iSCSI extension item reviewer approves and forwards to IANA for registration. IANA also acts when the IESG tells it that an appeal has overturned the iSCSI extension item reviewer's ruling ๐Ÿ‘‘

References

Normative References ๐Ÿ“š

Okay bestie, these are the docs this spec literally can't function without, so here's the full list, exactly as it's written ๐Ÿซถ

   [CAM]          ANSI X3.232-199X, Common Access Method-3.

   [EUI]          "Guidelines for 64-bit Global Identifier (EUI-64)",
                  http:
                  //standards.ieee.org/regauth/oui/tutorials/EUI64.html

   [OUI]          "IEEE OUI and Company_Id Assignments",
                  http://standards.ieee.org/regauth/oui

   [RFC791]       Postel, J., "Internet Protocol", STD 5, RFC 791,
                  September 1981.

   [RFC793]       Postel, J., "Transmission Control Protocol", STD 7,
                  RFC 793, September 1981.

   [RFC1035]      Mockapetris, P., "Domain Names - Implementation and
                  Specification", STD 13, RFC 1035, November 1987.

   [RFC1122]      Braden, R., Ed., "Requirements for Internet Hosts-
                  Communication Layer", STD 3, RFC 1122, October 1989.

   [RFC1510]      Kohl, J. and C. Neuman, "The Kerberos Network
                  Authentication Service (V5)", RFC 1510, September
                  1993.

   [RFC1737]      Sollins, K. and L. Masinter "Functional Requirements
                  for Uniform Resource Names"RFC 1737, December 1994.

   [RFC1964]      Linn, J., "The Kerberos Version 5 GSS-API Mechanism",
                  RFC 1964, June 1996.

   [RFC1982]      Elz, R. and R. Bush, "Serial Number Arithmetic", RFC
                  1982, August 1996.

   [RFC1994]      Simpson, W., "PPP Challenge Handshake Authentication
                  Protocol (CHAP)", RFC 1994, August 1996.

   [RFC2025]      Adams, C., "The Simple Public-Key GSS-API Mechanism
                  (SPKM)", RFC 2025, October 1996.

   [RFC2045]      Borenstein, N. and N. Freed, "MIME (Multipurpose
                  Internet Mail Extensions) Part One: Mechanisms for
                  Specifying and Describing the Format of Internet
                  Message Bodies", RFC 2045, November 1996.

   [RFC2119]      Bradner, S. "Key Words for use in RFCs to Indicate
                  Requirement Levels", BCP 14, RFC 2119, March 1997.

   [RFC2279]      Yergeau, F., "UTF-8, a Transformation Format of ISO
                  10646", RFC 2279 October 1996.

   [RFC2373]      Hinden, R. and S. Deering, "IP Version 6 Addressing
                  Architecture", RFC 2373, July 1998.

   [RFC2396]      Berners-Lee, T., Fielding, R. and L. Masinter "Uniform
                  Resource Identifiers", RFC 2396, August 1998.

   [RFC2401]      Kent, S. and R. Atkinson, "Security Architecture for
                  the Internet Protocol", RFC 2401, November 1998.

   [RFC2404]      Madson, C. and R. Glenn, "The Use of HMAC-SHA-1-96
                  within ESP and AH", RFC 2404, November 1998.

   [RFC2406]      Kent, S. and R. Atkinson, "IP Encapsulating Security
                  Payload (ESP)", RFC 2406, November 1998.

   [RFC2407]      Piper, D., "The Internet IP Security Domain of
                  Interpretation of ISAKMP", RFC 2407, November 1998.

   [RFC2409]      Harkins, D. and D. Carrel, "The Internet Key Exchange
                  (IKE)", RFC2409, November 1998.

   [RFC2434]      Narten, T. and H. Alvestrand, "Guidelines for Writing
                  an IANA Considerations Section in RFCs.", BCP 26, RFC
                  2434, October 1998.

   [RFC2451]      Pereira, R. and R. Adams " The ESP CBC-Mode Cipher
                  Algorithms", RFC 2451, November 1998.

   [RFC2732]      Hinden, R., Carpenter, B. and L. Masinter, "Format for
                  Literal IPv6 Addresses in URL's", RFC 2451, December
                  1999.

   [RFC2945]      Wu, T., "The SRP Authentication and Key Exchange
                  System", RFC 2945, September 2000.

   [RFC3066]      Alvestrand, H., "Tags for the Identification of
                  Languages", STD 47, RFC 3066, January 2001.

   [RFC3454]      Hoffman, P. and M. Blanchet, "Preparation of
                  Internationalized Strings ("stringprep")", RFC 3454,
                  December 2002.

   [RFC3566]      Frankel, S. and H. Herbert, "The AES-XCBC-MAC-96
                  Algorithm and Its Use With IPsec", RFC 3566, September
                  2003.

   [RFC3686]      Housley, R., "Using Advanced Encryption Standard (AES)
                  Counter Mode with IPsec Encapsulating Security Payload
                  (ESP)", RFC 3686, January 2004.

   [RFC3722]      Bakke, M., "String Profile for Internet Small Computer
                  Systems Interface (iSCSI) Names", RFC 3722, March
                  2004.

   [RFC3723]      Aboba, B., Tseng, J., Walker, J., Rangan, V. and F.
                  Travostino, "Securing Block Storage Protocols over
                  IP", RFC 3723, March 2004.

   [SAM2]         T10/1157D, SCSI Architecture Model - 2 (SAM-2).

   [SBC]          NCITS.306-1998, SCSI-3 Block Commands (SBC).

   [SPC3]         T10/1416-D, SCSI Primary Commands-3.

   [UNICODE]      Unicode Standard Annex #15, "Unicode Normalization
                  Forms", http://www.unicode.org/unicode/reports/tr15

Informative References ๐ŸŒธ

Now the informative ones, which are good background reading but not required to implement ๐Ÿ’…

   [BOOT]         P. Sarkar, et al., "Bootstrapping Clients using the
                  iSCSI Protocol", Work in Progress, July 2003.

   [Castagnoli93] G. Castagnoli, S. Braeuer and M. Herrman "Optimization
                  of Cyclic Redundancy-Check Codes with 24 and 32 Parity
                  Bits", IEEE Transact. on Communications, Vol. 41, No.
                  6, June 1993.

   [CORD]          Chadalapaka, M. and R. Elliott, "SCSI Command
                  Ordering Considerations with iSCSI", Work in Progress.

   [RFC3347]      Krueger, M., Haagens, R., Sapuntzakis, C. and M.
                  Bakke, "Small Computer Systems Interface protocol over
                  the Internet (iSCSI) Requirements and Design
                  Considerations", RFC 3347, July 2002.

   [RFC3385]      Sheinwald, D., Staran, J., Thaler, P. and V. Cavanna,
                  "Internet Protocol Small Computer System Interface
                  (iSCSI) Cyclic Redundancy Check (CRC)/Checksum
                  Considerations", RFC 3385, September 2002.

   [RFC3721]      Bakke M., Hafner, J., Hufferd, J., Voruganti, K. and
                  M. Krueger, "Internet Small Computer Systems Interface
                  (iSCSI) Naming and Discovery, RFC 3721, March 2004.

   [SEQ-EXT]      Kent, S., "IP Encapsulating Security Payload (ESP)",
                  Work in Progress, July 2002.

Appendix A. Sync and Steering with Fixed Interval Markers โœจ

Okay so this appendix is giving sync and steering ๐Ÿงš It's a simple scheme for synchronization, which is basically finding where each PDU starts. It uses markers carrying synchronization info, dropped at fixed intervals in the TCP stream.

A Marker is made up of the following:

   Byte /    0       |       1       |       2       |       3       |
       /             |               |               |               |
     |0 1 2 3 4 5 6 7|0 1 2 3 4 5 6 7|0 1 2 3 4 5 6 7|0 1 2 3 4 5 6 7|
     +---------------+---------------+---------------+---------------+
    0| Next-iSCSI-PDU-start pointer - copy #1                        |
     +---------------+---------------+---------------+---------------+
    4| Next-iSCSI-PDU-start pointer - copy #2                        |
     +---------------+---------------+---------------+---------------+

The Marker scheme counts bytes in the payload stream, and that count includes every byte iSCSI places in the TCP stream, except the markers themselves ๐Ÿ™ˆ It also skips any bytes TCP counts that iSCSI didn't originate.

Markers MUST NOT be included in digest calculation. ๐Ÿ’€

The Marker tells you the offset to the next iSCSI PDU header. It's eight bytes long and has two 32-bit offset fields, each saying how many bytes to skip in the TCP stream to find that next header. It keeps two copies of the pointer so that a marker split across a TCP packet boundary still leaves at least one valid copy in one of the packets. Iconic redundancy behavior ๐Ÿ‘‘

The structure and meaning of an inserted marker don't depend on the marker interval at all.

The use of markers is negotiable. The initiator and target MAY indicate whether they're ready to receive and/or send markers during login, and they do this separately for each connection. The default is No.

A.1. Markers At Fixed Intervals ๐Ÿฆ‹

A marker gets inserted at fixed intervals in the TCP byte stream. During login, each end of the iSCSI session says what interval it's willing to receive markers at, or it turns markers off completely. If a receiver asks for markers, the sender MAY agree during negotiation and provide markers at the requested interval. Heads up though: in certain environments, a sender that doesn't give markers to a receiver who wants them might take a pretty noticeable performance hit ๐Ÿ˜ญ

The marker interval and the initial marker-less interval are both counted in terms of the bytes iSCSI places in the TCP stream data.

When it comes down to iSCSI terms, markers MUST indicate the offset to a 4-byte word boundary in the stream. The two least significant bits of each marker word are reserved and count as 0 for the offset computation. โœจ

Padding iSCSI PDU payloads out to 4-byte word boundaries makes marker manipulation way simpler ๐Ÿ“

A.2. Initial Marker-less Interval ๐Ÿงš

So, to let the connection setup (including the Login Phase negotiation) go smoothly, marking (if any) only starts at the first marker interval after the Login Phase ends. But here's the twist: so the marker inclusion and exclusion mechanism can work without knowing how long the Login Phase was, the first marker gets placed in the TCP stream as if the Marker-less interval had included markers. Galaxy brain move ๐ŸŒˆ

That means all markers show up in the stream at locations matching this formula: [(MI + 8) * n - 8], where MI = Marker Interval and n = integer number.

For example, if the marker interval is 512 bytes and the login ended at byte 1003 (first iSCSI placed byte is 0), the first marker will be inserted after byte 1031 in the stream.

A.3. Negotiation ๐Ÿ’œ

The following operational key=value pairs are used to negotiate the fixed interval markers. The direction (output or input) is relative to the initiator.

A.3.1. OFMarker, IFMarker ๐ŸŽ€

   Use: IO
   Senders: Initiator and Target
   Scope: CO

   OFMarker=<boolean-value>
   IFMarker=<boolean-value>

Default is No. Result function is AND. ๐Ÿ’…

OFMarker turns the initiator-to-target markers on or off on the connection. IFMarker turns the target-to-initiator markers on or off on the connection. Here's how it plays out:

   Examples:

     I->OFMarker=Yes,IFMarker=Yes
     T->OFMarker=Yes,IFMarker=Yes

   Results in the Marker being used in both directions while:

     I->OFMarker=Yes,IFMarker=Yes
     T->OFMarker=Yes,IFMarker=No

   Results in Marker being used from the initiator to the target, but
   not from the target to initiator.

A.3.2. OFMarkInt, IFMarkInt

Use: IO
Senders: Initiator and Target
Scope: CO
OFMarkInt is Irrelevant when: OFMarker=No
IFMarkInt is Irrelevant when: IFMarker=No

Offering:

OFMarkInt=<numeric-range-from-1-to-65535>
IFMarkInt=<numeric-range-from-1-to-65535>

Responding:

OFMarkInt=<numeric-value-from-1-to-65535>|Reject
IFMarkInt=<numeric-value-from-1-to-65535>|Reject

okay so OFMarkInt sets the marker interval for initiator-to-target markers on the connection, and IFMarkInt sets it for target-to-initiator markers ๐Ÿ’–

when you're offering, the initiator or target says the minimum and maximum interval it wants (counted in 4-byte words) for one or both directions ๐Ÿงš if it only wants one exact value, it just sends that single value โœจ the responder then picks something inside the min-to-max range you offered (or the single value you sent), or it says through the xFMarker key=value that it can't set and/or receive markers ๐Ÿ™ˆ if the interval is unacceptable, the responder answers with "Reject" ๐Ÿ’€ and Reject basically resets the marker function in that direction (Output or Input) back to No ๐Ÿซถ

the interval gets measured from the end of one marker to the start of the next marker. so a value of 1024 means 1024 words, which is 4096 bytes of iSCSI payload between markers ๐ŸŒธ

the default is 2048 ๐ŸŽ€


Appendix B. Examples ๐Ÿฆ‹

B.1. Read Operation Example

+------------------+-----------------------+----------------------+
|Initiator Function|    PDU Type           |  Target Function     |
+------------------+-----------------------+----------------------+
|  Command request |SCSI Command (READ)>>> |                      |
|  (read)          |                       |                      |
+------------------+-----------------------+----------------------+
|                  |                       |Prepare Data Transfer |
+------------------+-----------------------+----------------------+
|   Receive Data   |   <<< SCSI Data-In    |   Send Data          |
+------------------+-----------------------+----------------------+
|   Receive Data   |   <<< SCSI Data-In    |   Send Data          |
+------------------+-----------------------+----------------------+
|   Receive Data   |   <<< SCSI Data-In    |   Send Data          |
+------------------+-----------------------+----------------------+
|                  |   <<< SCSI Response   |Send Status and Sense |
+------------------+-----------------------+----------------------+
| Command Complete |                       |                      |
+------------------+-----------------------+----------------------+

B.2. Write Operation Example

+------------------+-----------------------+---------------------+
|Initiator Function|    PDU Type           |  Target Function    |
+------------------+-----------------------+---------------------+
| Command request  |SCSI Command (WRITE)>>>| Receive command     |
|  (write)         |                       | and queue it        |
+------------------+-----------------------+---------------------+
|                  |                       | Process old commands|
+------------------+-----------------------+---------------------+
|                  |                       | Ready to process    |
|                  |   <<< R2T             | WRITE command       |
+------------------+-----------------------+---------------------+
|   Send Data      |   SCSI Data-Out >>>   |   Receive Data      |
+------------------+-----------------------+---------------------+
|                  |   <<< R2T             | Ready for data      |
+------------------+-----------------------+---------------------+
|                  |   <<< R2T             | Ready for data      |
+------------------+-----------------------+---------------------+
|   Send Data      |   SCSI Data-Out >>>   |   Receive Data      |
+------------------+-----------------------+---------------------+
|   Send Data      |   SCSI Data-Out >>>   |   Receive Data      |
+------------------+-----------------------+---------------------+
|                  |   <<< SCSI Response   |Send Status and Sense|
+------------------+-----------------------+---------------------+
| Command Complete |                       |                     |
+------------------+-----------------------+---------------------+

B.3. R2TSN/DataSN Use Examples

Output (write) data DataSN/R2TSN Example

+------------------+-----------------------+----------------------+
|Initiator Function|    PDU Type & Content |  Target Function     |
+------------------+-----------------------+----------------------+
|  Command request |SCSI Command (WRITE)>>>| Receive command      |
|  (write)         |                       | and queue it         |
+------------------+-----------------------+----------------------+
|                  |                       | Process old commands |
+------------------+-----------------------+----------------------+
|                  |   <<< R2T             | Ready for data       |
|                  |   R2TSN = 0           |                      |
+------------------+-----------------------+----------------------+
|                  |   <<< R2T             | Ready for more data  |
|                  |   R2TSN = 1           |                      |
+------------------+-----------------------+----------------------+
|  Send Data       |   SCSI Data-Out >>>   |   Receive Data       |
|  for R2TSN 0     |   DataSN = 0, F=0     |                      |
+------------------+-----------------------+----------------------+
|  Send Data       |   SCSI Data-Out >>>   |   Receive Data       |
|  for R2TSN 0     |   DataSN = 1, F=1     |                      |
+------------------+-----------------------+----------------------+
|  Send Data       |   SCSI Data >>>       |   Receive Data       |
|  for R2TSN 1     |   DataSN = 0, F=1     |                      |
+------------------+-----------------------+----------------------+
|                  |   <<< SCSI Response   |Send Status and Sense |
|                  |   ExpDataSN = 0       |                      |
+------------------+-----------------------+----------------------+
| Command Complete |                       |                      |
+------------------+-----------------------+----------------------+

Input (read) data DataSN Example

+------------------+-----------------------+----------------------+
|Initiator Function|    PDU Type           |  Target Function     |
+------------------+-----------------------+----------------------+
|  Command request |SCSI Command (READ)>>> |                      |
|  (read)          |                       |                      |
+------------------+-----------------------+----------------------+
|                  |                       | Prepare Data Transfer|
+------------------+-----------------------+----------------------+
|   Receive Data   |   <<< SCSI Data-In    |   Send Data          |
|                  |   DataSN = 0, F=0     |                      |
+------------------+-----------------------+----------------------+
|   Receive Data   |   <<< SCSI Data-In    |   Send Data          |
|                  |   DataSN = 1, F=0     |                      |
+------------------+-----------------------+----------------------+
|   Receive Data   |   <<< SCSI Data-In    |   Send Data          |
|                  |   DataSN = 2, F=1     |                      |
+------------------+-----------------------+----------------------+
|                  |   <<< SCSI Response   |Send Status and Sense |
|                  |   ExpDataSN = 3       |                      |
+------------------+-----------------------+----------------------+
| Command Complete |                       |                      |
+------------------+-----------------------+----------------------+

Bidirectional DataSN Example

+------------------+-----------------------+----------------------+
|Initiator Function|    PDU Type           | Target Function      |
+------------------+-----------------------+----------------------+
| Command request |SCSI Command >>>        |                      |
| (Read-Write)     | Read-Write            |                      |
+------------------+-----------------------+----------------------+
|                  |                       | Process old commands |
+------------------+-----------------------+----------------------+
|                  |   <<< R2T             | Ready to process     |
|                  |   R2TSN = 0           | WRITE command        |
+------------------+-----------------------+----------------------+
| * Receive Data   |   <<< SCSI Data-In    |   Send Data          |
|                  |   DataSN = 1, F=0     |                      |
+------------------+-----------------------+----------------------+
| * Receive Data   |   <<< SCSI Data-In    |   Send Data          |
|                  |   DataSN = 2, F=1     |                      |
+------------------+-----------------------+----------------------+
| * Send Data      |   SCSI Data-Out >>>   |   Receive Data       |
| for R2TSN 0      |   DataSN = 0, F=1     |                      |
+------------------+-----------------------+----------------------+
|                  |   <<< SCSI Response   |Send Status and Sense |
|                  |   ExpDataSN = 3       |                      |
+------------------+-----------------------+----------------------+
| Command Complete |                       |                      |
+------------------+-----------------------+----------------------+

*) okay so the Send Data and Receive Data can happen at the same time (that's the atomic Read-Old-Write-New vibe) ๐Ÿ”ฅ or they can go one after the other (the atomic Read-Update-Write way), and in that version the R2T can show up after the data you received โœจ

Unsolicited and immediate output (write) data with DataSN Example

+------------------+-----------------------+----------------------+
|Initiator Function|    PDU Type & Content |  Target Function     |
+------------------+-----------------------+----------------------+
|  Command request |SCSI Command (WRITE)>>>| Receive command      |
|  (write)         |F=0                    | and data             |
|+ Immediate data  |                       | and queue it         |
+------------------+-----------------------+----------------------+
| Send Unsolicited |   SCSI Write Data >>> | Receive more Data    |
|  Data            |   DataSN = 0, F=1     |                      |
+------------------+-----------------------+----------------------+
|                  |                       | Process old commands |
+------------------+-----------------------+----------------------+
|                  |   <<< R2T             | Ready for more data  |
|                  |   R2TSN = 0           |                      |
+------------------+-----------------------+----------------------+
|  Send Data       |   SCSI Write Data >>> |   Receive Data       |
|  for R2TSN 0     |   DataSN = 0, F=1     |                      |
+------------------+-----------------------+----------------------+
|                  |   <<< SCSI Response   |Send Status and Sense |
|                  |                       |                      |
+------------------+-----------------------+----------------------+
| Command Complete |                       |                      |
+------------------+-----------------------+----------------------+

B.4. CRC Examples โœจ

okay bestie, time for the CRC examples ๐Ÿงฎ๐Ÿ’– N.B. all Values are Hexadecimal ๐ŸŒธ so every number below is base 16, obvi!

first up: 32 bytes of zeroes ๐Ÿซถ

     Byte:        0  1  2  3

        0:       00 00 00 00
      ...
       28:       00 00 00 00

      CRC:       aa 36 91 8a

next, 32 bytes of ones ๐Ÿ”ฅ

     Byte:        0  1  2  3

        0:       ff ff ff ff
      ...
       28:       ff ff ff ff

      CRC:       43 ab a8 62

now the 32 bytes of incrementing 00..1f ๐ŸŒˆ it's giving counting up, and the CRC is different for each one ๐Ÿ˜ญ

     Byte:        0  1  2  3

        0:       00 01 02 03
      ...
       28:       1c 1d 1e 1f

      CRC:       4e 79 dd 46

and the mirror version, 32 bytes of decrementing 1f..00 ๐Ÿฆ‹ counting down, same bytes, totally different CRC, iykyk

     Byte:        0  1  2  3

        0:       1f 1e 1d 1c
      ...
       28:       03 02 01 00

      CRC:       5c db 3f 11

last but not least, a real one: An iSCSI - SCSI Read (10) Command PDU ๐Ÿ‘‘ this is the main character example, so pay attention ๐Ÿ’…

    Byte:        0  1  2  3

       0:       01 c0 00 00
       4:       00 00 00 00
       8:       00 00 00 00
      12:       00 00 00 00
      16:       14 00 00 00
      20:       00 00 04 00
      24:       00 00 00 14
      28:       00 00 00 18
      32:       28 00 00 00
      36:       00 00 00 00
      40:       02 00 00 00
      44:       00 00 00 00

     CRC:       56 3a 96 d9

Appendix C. Login Phase Examples โœจ

okay so this appendix is basically a showcase of every way two devices can log in to each other, and the whole thing is about who proves they are who they say they are ๐Ÿ”๐Ÿ’… we'll go one method at a time.

Kerberos, both ways ๐Ÿ‘‘ The initiator and target authenticate each other via Kerberos. This one is super thorough, bestie.

I-> Login (CSG,NSG=0,1 T=1)
    InitiatorName=iqn.1999-07.com.os:hostid.77
    TargetName=iqn.1999-07.com.example:diskarray.sn.88
    AuthMethod=KRB5,SRP,None

T-> Login (CSG,NSG=0,0 T=0)
    AuthMethod=KRB5

I-> Login (CSG,NSG=0,1 T=1)
    KRB_AP_REQ=<krb_ap_req>

(krb_ap_req contains the Kerberos V5 ticket and authenticator
   with MUTUAL-REQUIRED set in the ap-options field)

If the authentication is successful, the target proceeds with:

T-> Login (CSG,NSG=0,1 T=1)
    KRB_AP_REP=<krb_ap_rep>

(krb_ap_rep is the Kerberos V5 mutual authentication reply)

If the authentication is successful, the initiator may proceed with:

I-> Login (CSG,NSG=1,0 T=0) FirstBurstLength=8192
T-> Login (CSG,NSG=1,0 T=0) FirstBurstLength=4096
     MaxBurstLength=8192
I-> Login (CSG,NSG=1,0 T=0) MaxBurstLength=8192
    ... more iSCSI Operational Parameters

T-> Login (CSG,NSG=1,0 T=0)
    ... more iSCSI Operational Parameters

And at the end:

I-> Login (CSG,NSG=1,3 T=1)
    optional iSCSI parameters

T-> Login (CSG,NSG=1,3 T=1) "login accept"

If the initiator's authentication by the target is not successful, the target responds with:

T-> Login "login reject"

instead of the Login KRB_AP_REP message, and terminates the connection.

If the target's authentication by the initiator is not successful, the initiator terminates the connection (without responding to the Login KRB_AP_REP message).

Kerberos, one way ๐Ÿฆ‹ In this one, only the initiator is authenticated by the target via Kerberos. Less back-and-forth, same vibes.

I-> Login (CSG,NSG=0,1 T=1)
   InitiatorName=iqn.1999-07.com.os:hostid.77
   TargetName=iqn.1999-07.com.example:diskarray.sn.88
   AuthMethod=SRP,KRB5,None

T-> Login-PR (CSG,NSG=0,0 T=0)
   AuthMethod=KRB5

I-> Login (CSG,NSG=0,1 T=1)
    KRB_AP_REQ=krb_ap_req

(MUTUAL-REQUIRED not set in the ap-options field of krb_ap_req)

If the authentication is successful, the target proceeds with:

T-> Login (CSG,NSG=0,1 T=1)

I-> Login (CSG,NSG=1,0 T=0)
    ... iSCSI parameters

T-> Login (CSG,NSG=1,0 T=0)
    ... iSCSI parameters

. . .

T-> Login (CSG,NSG=1,3 T=1)"login accept"

SPKM1 ๐ŸŒธ Here the initiator and target authenticate each other via SPKM1, and it's giving very structured token energy.

I-> Login (CSG,NSG=0,1 T=1)
    InitiatorName=iqn.1999-07.com.os:hostid.77
    TargetName=iqn.1999-07.com.example:diskarray.sn.88
    AuthMethod=SPKM1,KRB5,None

T-> Login (CSG,NSG=0,0 T=0)
    AuthMethod=SPKM1

I-> Login (CSG,NSG=0,0 T=0)
    SPKM_REQ=<spkm-req>

(spkm-req is the SPKM-REQ token with the mutual-state bit in the
   options field of the REQ-TOKEN set)

T-> Login (CSG,NSG=0,0 T=0)
    SPKM_REP_TI=<spkm-rep-ti>

If the authentication is successful, the initiator proceeds:

I-> Login (CSG,NSG=0,1 T=1)
    SPKM_REP_IT=<spkm-rep-it>

If the authentication is successful, the target proceeds with:

T-> Login (CSG,NSG=0,1 T=1)

The initiator may proceed:

I-> Login  (CSG,NSG=1,0 T=0) ... iSCSI parameters
T-> Login  (CSG,NSG=1,0 T=0) ... iSCSI parameters

And at the end:

I-> Login  (CSG,NSG=1,3 T=1)
    optional iSCSI parameters

T-> Login (CSG,NSG=1,3 T=1) "login accept"

If the target's authentication by the initiator is not successful, the initiator terminates the connection (without responding to the Login SPKM_REP_TI message).

If the initiator's authentication by the target is not successful, the target responds with:

T-> Login "login reject"

instead of the Login "proceed and change stage" message, and terminates the connection.

SPKM2 ๐Ÿ’œ Next up, the initiator and target authenticate each other via SPKM2. Same squad, slightly different moves.

I-> Login (CSG,NSG=0,0 T=0)
    InitiatorName=iqn.1999-07.com.os:hostid.77
    TargetName=iqn.1999-07.com.example:diskarray.sn.88
          AuthMethod=SPKM1,SPKM2

T-> Login-PR (CSG,NSG=0,0 T=0)
    AuthMethod=SPKM2

I-> Login (CSG,NSG=0,1 T=1)
    SPKM_REQ=<spkm-req>

(spkm-req is the SPKM-REQ token with the mutual-state bit in the
     options field of the REQ-TOKEN not set)

If the authentication is successful, the target proceeds with:

T-> Login (CSG,NSG=0,1 T=1)

The initiator may proceed:

I-> Login (CSG,NSG=1,0 T=0)
    ... iSCSI parameters

T-> Login (CSG,NSG=1,0 T=0)
    ... iSCSI parameters

And at the end:

I-> Login  (CSG,NSG=1,3 T=1)
    optional iSCSI parameters

T-> Login (CSG,NSG=1,3 T=1) "login accept"

SRP, full mutual vibes ๐Ÿ”ฅ In this example the initiator and target authenticate each other via SRP. The target gets verified too, because TargetAuth=Yes is on the table.

I-> Login (CSG,NSG=0,1 T=1)
    InitiatorName=iqn.1999-07.com.os:hostid.77
    TargetName=iqn.1999-07.com.example:diskarray.sn.88
    AuthMethod=KRB5,SRP,None

T-> Login-PR (CSG,NSG=0,0 T=0)
    AuthMethod=SRP

I-> Login (CSG,NSG=0,0 T=0)
    SRP_U=<user>
    TargetAuth=Yes

T-> Login (CSG,NSG=0,0 T=0)
    SRP_GROUP=SRP-1536,SRP-1024
    SRP_s=<s>

I-> Login (CSG,NSG=0,0 T=0)
    SRP_GROUP=SRP-1536
    SRP_A=<A>

T-> Login (CSG,NSG=0,0 T=0)
    SRP_B=<B>

I-> Login (CSG,NSG=0,1 T=1)
    SRP_M=<M>

If the initiator authentication is successful, the target proceeds:

T-> Login (CSG,NSG=0,1 T=1)
    SRP_HM=<H(A | M | K)>

Where N, g, s, A, B, M, and H(A | M | K) are defined in [RFC2945].

If the target authentication is not successful, the initiator terminates the connection; otherwise, it proceeds.

I-> Login (CSG,NSG=1,0 T=0)
    ... iSCSI parameters

T-> Login (CSG,NSG=1,0 T=0)
    ... iSCSI parameters

And at the end:

I-> Login (CSG,NSG=1,3 T=1)
    optional iSCSI parameters

T-> Login  (CSG,NSG=1,3 T=1) "login accept"

If the initiator authentication is not successful, the target responds with:

T-> Login "login reject"

Instead of the T-> Login SRP_HM=<H(A | M | K)> message, it terminates the connection.

SRP, target not checked ๐ŸŒˆ Another SRP exchange, but this time TargetAuth=No, so the target skips the extra verification step.

I-> Login (CSG,NSG=0,1 T=1)
    InitiatorName=iqn.1999-07.com.os:hostid.77
    TargetName=iqn.1999-07.com.example:diskarray.sn.88
    AuthMethod=KRB5,SRP,None

T-> Login-PR (CSG,NSG=0,0 T=0)
    AuthMethod=SRP

I-> Login (CSG,NSG=0,0 T=0)
    SRP_U=<user>
    TargetAuth=No

 T-> Login (CSG,NSG=0,0 T=0)
     SRP_GROUP=SRP-1536
     SRP_s=<s>

I-> Login (CSG,NSG=0,0 T=0)
    SRP_GROUP=SRP-1536
    SRP_A=<A>

T-> Login (CSG,NSG=0,0 T=0)
    SRP_B=<B>

I-> Login (CSG,NSG=0,1 T=1)
    SRP_M=<M>

If the initiator authentication is successful, the target proceeds:

T-> Login (CSG,NSG=0,1 T=1)
I-> Login (CSG,NSG=1,0 T=0)
    ... iSCSI parameters

T-> Login (CSG,NSG=1,0 T=0)
    ... iSCSI parameters

And at the end:

I-> Login (CSG,NSG=1,3 T=1)
    optional iSCSI parameters

T-> Login (CSG,NSG=1,3 T=1) "login accept"

CHAP, mutual ๐ŸŽ€ In the next example the initiator and target authenticate each other via CHAP. Lots of little challenge-and-response moments here.

I-> Login (CSG,NSG=0,0 T=0)
    InitiatorName=iqn.1999-07.com.os:hostid.77
    TargetName=iqn.1999-07.com.example:diskarray.sn.88
    AuthMethod=KRB5,CHAP,None

T-> Login-PR (CSG,NSG=0,0 T=0)
    AuthMethod=CHAP

I-> Login (CSG,NSG=0,0 T=0)
    CHAP_A=<A1,A2>

T-> Login (CSG,NSG=0,0 T=0)
    CHAP_A=<A1>
    CHAP_I=<I>
    CHAP_C=<C>

I-> Login (CSG,NSG=0,1 T=1)
    CHAP_N=<N>
    CHAP_R=<R>
    CHAP_I=<I>
    CHAP_C=<C>

If the initiator authentication is successful, the target proceeds:

T-> Login (CSG,NSG=0,1 T=1)
    CHAP_N=<N>
    CHAP_R=<R>

If the target authentication is not successful, the initiator aborts the connection; otherwise, it proceeds.

I-> Login (CSG,NSG=1,0 T=0)
    ... iSCSI parameters
T-> Login (CSG,NSG=1,0 T=0)
    ... iSCSI parameters

And at the end:

I-> Login (CSG,NSG=1,3 T=1)
    optional iSCSI parameters

T-> Login (CSG,NSG=1,3 T=1) "login accept"

If the initiator authentication is not successful, the target responds with:

T-> Login "login reject"

Instead of the Login CHAP_R=<response> "proceed and change stage" message, it terminates the connection.

CHAP, one way ๐Ÿฆ„ In the next example, only the initiator is authenticated by the target via CHAP.

I-> Login (CSG,NSG=0,1 T=0)
    InitiatorName=iqn.1999-07.com.os:hostid.77
    TargetName=iqn.1999-07.com.example:diskarray.sn.88
    AuthMethod=KRB5,CHAP,None

T-> Login-PR (CSG,NSG=0,0 T=0)
    AuthMethod=CHAP

I-> Login (CSG,NSG=0,0 T=0)
    CHAP_A=<A1,A2>

T-> Login (CSG,NSG=0,0 T=0)
    CHAP_A=<A1>
    CHAP_I=<I>
    CHAP_C=<C>

I-> Login (CSG,NSG=0,1 T=1)
    CHAP_N=<N>
    CHAP_R=<R>

If the initiator authentication is successful, the target proceeds:

T-> Login (CSG,NSG=0,1 T=1)

I-> Login (CSG,NSG=1,0 T=0)
    ... iSCSI parameters

T-> Login (CSG,NSG=1,0 T=0)
    ... iSCSI parameters

And at the end:

I-> Login (CSG,NSG=1,3 T=1)
    optional iSCSI parameters

T-> Login (CSG,NSG=1,3 T=1) "login accept"

No security, straight to the good stuff ๐Ÿ“ In the next example, the initiator does not offer any security parameters. It therefore may offer iSCSI parameters on the Login PDU with the T bit set to 1, and the target may respond with a final Login Response PDU immediately.

I-> Login (CSG,NSG=1,3 T=1)
    InitiatorName=iqn.1999-07.com.os:hostid.77
    TargetName=iqn.1999-07.com.example:diskarray.sn.88
    ... iSCSI parameters

T-> Login (CSG,NSG=1,3 T=1) "login accept"
    ... ISCSI parameters

Security offered, but "None" chosen ๐Ÿงš In the last example, the initiator does offer security parameters on the Login PDU, but the target does not choose any (i.e., chooses the "None" values).

I-> Login (CSG,NSG=0,1 T=1)
    InitiatorName=iqn.1999-07.com.os:hostid.77
    TargetName=iqn.1999-07.com.example:diskarray.sn.88
    AuthMethod=KRB5,SRP,None

T-> Login-PR (CSG,NSG=0,1 T=1)
    AuthMethod=None

I-> Login (CSG,NSG=1,0 T=0)
    ... iSCSI parameters

T-> Login (CSG,NSG=1,0 T=0)
    ... iSCSI parameters

And at the end:

I-> Login (CSG,NSG=1,3 T=1)
    optional iSCSI parameters

T-> Login (CSG,NSG=1,3 T=1) "login accept"

Appendix D. SendTargets Operation ๐Ÿงš

Okay bestie, so to cut down on how much config an initiator has to do by hand, iSCSI gives us the SendTargets text request ๐Ÿ’… The initiator uses it to ask for a list of targets it might have access to, plus the addresses (IP address and TCP port) where those targets can be reached ๐Ÿฆ‹

To use SendTargets, the initiator has to set up one of two session types first ๐ŸŽ€ If it establishes the session with the key SessionType=Discovery, that's a discovery session and you don't have to name a target. Otherwise it's a normal, operational session. The SendTargets command MUST only be sent during the Full Feature Phase of a normal or discovery session ๐Ÿ”ฅ

A system that has targets MUST support discovery sessions on each of its iSCSI IP address-port pairs, and it MUST support the SendTargets command on the discovery session. In a discovery session, a target MUST return all path information (target name and IP address-port pairs and portal group tags) for the targets on the target network entity that the requesting initiator is authorized to access. Main character energy ๐Ÿ‘‘

A target MUST support the SendTargets command on operational sessions. These only return path information about the target the session is connected to, and they don't need to return info about other target names that might be defined in the responding system ๐ŸŒธ

An initiator MAY make use of SendTargets as it sees fit โœจ

A SendTargets command is a single Text request PDU. This PDU contains exactly one text key and value. The text key MUST be SendTargets. The expected response depends on the value, and also on whether the session is a discovery or operational one ๐Ÿงš

The value must be one of:

  • All: The initiator is asking for info on all relevant targets the implementation knows about. This value MUST be supported on a discovery session, and MUST NOT be supported on an operational session.
  • <iSCSI-target-name>: If an iSCSI target name is specified, the session should respond with addresses for only the named target, if possible. This value MUST be supported on discovery sessions. A discovery session MUST be capable of returning addresses for those targets that would have been returned had value=All been designated.
  • <nothing>: The session should only respond with addresses for the target it's logged in to. This MUST be supported on operational sessions, and MUST NOT return targets other than the one the session is logged in to.

The response is a text response with zero or more targets and, optionally, their addresses. Each target comes back as a target record. A target record starts with the TargetName text key, followed by a list of TargetAddress text keys, and it's bounded by the end of the text response or the next TargetName key, which starts a new record. No text keys other than TargetName and TargetAddress are permitted within a SendTargets response ๐Ÿ™ˆ

For the format of the TargetName, see Section 12.4 TargetName.

In a discovery session, a target MAY respond to a SendTargets request with its complete list of targets, or with a list of targets based on the name of the initiator logged in to the session ๐Ÿ’œ

A SendTargets response MUST NOT contain target names if there are no targets for the requesting initiator to access ๐Ÿ’€

Each target record returned includes zero or more TargetAddress fields ๐Ÿ“

Each target record starts with one text key of the form:

TargetName=<target-name-goes-here>

Followed by zero or more address keys of the form:

TargetAddress=<hostname-or-ipaddress>[:<tcp-port>],
  <portal-group-tag>

The hostname-or-ipaddress holds a domain name, IPv4 address, or IPv6 address, as specified for the TargetAddress key.

A hostname-or-ipaddress that shows up duplicated in TargetAddress responses for a given node (the port is absent or equal) would probably mean that multiple address families are in use at once (IPV6 and IPV4). Two families, one node, very efficient ๐Ÿฆ‹

Each TargetAddress belongs to a portal group, identified by its numeric portal group tag (as in Section 12.9 TargetPortalGroupTag). The iSCSI target name, together with this tag, makes up the SCSI port identifier. The tag only needs to be unique within a given target's name list of addresses ๐ŸŽ€

Multiple-connection sessions can span iSCSI addresses that belong to the same portal group.

Multiple-connection sessions cannot span iSCSI addresses that belong to different portal groups.

If a SendTargets response reports an iSCSI address for a target, it SHOULD also report all other addresses in its portal group in the same response ๐Ÿซถ

A SendTargets text response can be longer than a single Text Response PDU, and it makes use of the long text responses as specified ๐Ÿ“œ

After getting a list of targets from the discovery target session, an iSCSI initiator may start new sessions to log in to the discovered targets for full operation. The initiator MAY keep the discovery session open, and MAY send subsequent SendTargets commands to discover new targets ๐ŸŒˆ

Examples:

This example is the SendTargets response from a single target that has no other interface ports.

Initiator sends a text request that contains:

SendTargets=All

Target sends a text response that contains:

TargetName=iqn.1993-11.com.example:diskarray.sn.8675309

All the target had to return in the simple case was the target name. The initiator assumes that the IP address and TCP port for this target are the same as the ones used on the current connection to the default iSCSI target ๐Ÿ’–

The next example has two internal iSCSI targets, each accessible via two different ports with different IP addresses. The following is the text response:

   TargetName=iqn.1993-11.com.example:diskarray.sn.8675309
   TargetAddress=10.1.0.45:3000,1 TargetAddress=10.1.1.45:3000,2
   TargetName=iqn.1993-11.com.example:diskarray.sn.1234567
   TargetAddress=10.1.0.45:3000,1 TargetAddress=10.1.1.45:3000,2

Both targets share both addresses, and the multiple addresses are likely used to provide multi-path support. The initiator may connect to either target name on either address. Each of the addresses has its own portal group tag, and they do not support spanning multiple-connection sessions with each other. Keep in mind that the portal group tags for the two named targets are independent of one another. Portal group "1" on the first target is not necessarily the same as portal group "1" on the second target. Don't mix them up, bestie ๐Ÿ™ˆ

In the above example, a DNS host name or an IPv6 address could have been returned instead of an IPv4 address ๐Ÿงš

The next text response shows a target that supports spanning sessions across multiple addresses, and it further illustrates the use of portal group tags:

    TargetName=iqn.1993-11.com.example:diskarray.sn.8675309

   TargetAddress=10.1.0.45:3000,1 TargetAddress=10.1.1.46:3000,1
   TargetAddress=10.1.0.47:3000,2 TargetAddress=10.1.1.48:3000,2
   TargetAddress=10.1.1.49:3000,3

In this example, any of the target addresses can be used to reach the same target. A single-connection session can be established to any of these TCP addresses. A multiple-connection session could span addresses .45 and .46 or .47 and .48, but it cannot span any other combination. A TargetAddress with its own tag (.49) cannot be combined with any other address within the same session ๐Ÿฆ‹

This SendTargets response does not indicate whether .49 supports multiple connections per session. That is communicated via the MaxConnections text key upon login to the target ๐ŸŒธ


Appendix E. Algorithmic Presentation of Error Recovery Classes โœจ

This appendix illustrates the error recovery classes using a pseudo-programming-language. The procedure names are chosen to be obvious to most implementers. Each of the recovery classes described has initiator procedures as well as target procedures. These algorithms focus on outlining the mechanics of error recovery classes, and they don't exhaustively describe all other aspects or cases. Examples of this approach are:

  • Handling for only certain Opcode types is shown.
  • Only certain reason codes (e.g., Recovery in Logout command) are outlined.
  • Resultant cases, such as recovery of Synchronization on a header digest error, are considered out-of-scope in these algorithms. In this particular example, a header digest error may lead to connection recovery if some type of sync and steering layer is not implemented.

These algorithms try to convey the iSCSI error recovery concepts in the simplest terms, and they are not designed to be optimal ๐Ÿ’…

E.1. General Data Structure and Procedure Description

This section defines the procedures and data structures that are commonly used by all the error recovery algorithms. The structures may not be the exhaustive representations of what a typical implementation requires ๐ŸŽ€

Data structure definitions:

struct TransferContext {
        int TargetTransferTag;
        int ExpectedDataSN;
};

struct TCB {              /* task control block */
        Boolean SoFarInOrder;
        int ExpectedDataSN; /* used for both R2Ts, and Data */
        int MissingDataSNList[MaxMissingDPDU];
        Boolean FbitReceived;
        Boolean StatusXferd;
        Boolean CurrentlyAllegiant;
        int ActiveR2Ts;
        int Response;
        char *Reason;
        struct TransferContext
                    TransferContextList[MaxOutStandingR2T];
        int InitiatorTaskTag;
        int CmdSN;

        int SNACK_Tag;

};

struct Connection {
        struct Session SessionReference;
        Boolean SoFarInOrder;
        int CID;
        int State;

        int CurrentTimeout;
        int ExpectedStatSN;
        int MissingStatSNList[MaxMissingSPDU];
        Boolean PerformConnectionCleanup;
};

struct Session {
        int NumConnections;
        int CmdSN;
        int Maxconnections;
        int ErrorRecoveryLevel;
        struct iSCSIEndpoint OtherEndInfo;
        struct Connection ConnectionList[MaxSupportedConns];
};

Procedure descriptions:

Receive-a-In-PDU(transport connection, inbound PDU);
check-basic-validity(inbound PDU);
Start-Timer(timeout handler, argument, timeout value);
Build-And-Send-Reject(transport connection, bad PDU, reason code);

E.2. Within-command Error Recovery Algorithms

E.2.1.  Procedure Descriptions

Okay so this is the part where the target and initiator procedures get their names, and bestie, I'm obsessed ๐Ÿงš. Here's the full lineup of procedure descriptions, no cap:

   Recover-Data-if-Possible(last required DataSN, task control
   block);
   Build-And-Send-DSnack(task control block);
   Build-And-Send-RDSnack(task control block);
   Build-And-Send-Abort(task control block);
   SCSI-Task-Completion(task control block);
   Build-And-Send-A-Data-Burst(transport connection, data-descriptor,
                                                 task control block);

   Build-And-Send-R2T(transport connection, data-descriptor,
                                                task control block);
   Build-And-Send-Status(transport connection, task control block);
   Transfer-Context-Timeout-Handler(transfer context);

Notes:

  • One procedure used in this section: Handle-Status-SNACK-request is defined in Within-connection recovery algorithms. ๐ŸŽ€
  • The Response processing pseudo-code, shown in the target algorithms, applies to all solicited PDUs that carry StatSN - SCSI Response, Text Response etc. ๐Ÿ’…

E.2.2. Initiator Algorithms โœจ

Bestie, this is where the initiator figures out what's missing and asks for it back ๐Ÿซถ.

Recover-Data-if-Possible(LastRequiredDataSN, TCB)
{
  if (operational ErrorRecoveryLevel > 0) {
       if (# of missing PDUs is trackable) {
             Note the missing DataSNs in TCB.
             if (the task spanned a change in
                       MaxRecvDataSegmentLength) {
                  if (TCB.StatusXferd is TRUE)
                     drop the status PDU;
                  Build-And-Send-RDSnack(TCB);
             } else {
                  Build-And-Send-DSnack(TCB);
             }
       } else {
           TCB.Reason = "Protocol service CRC error";
           }
  } else {
        TCB.Reason = "Protocol service CRC error";
  }
  if (TCB.Reason == "Protocol service CRC error") {
        Clear the missing PDU list in the TCB.
        if (TCB.StatusXferd is not TRUE)
           Build-And-Send-Abort(TCB);
  }
}

Receive-a-In-PDU(Connection, CurrentPDU)
{
  check-basic-validity(CurrentPDU);
  if (Header-Digest-Bad) discard, return;
  Retrieve TCB for CurrentPDU.InitiatorTaskTag.

  if ((CurrentPDU.type == Data)
              or (CurrentPDU.type = R2T)) {
     if (Data-Digest-Bad for Data) {
           send-data-SNACK = TRUE;
       LastRequiredDataSN = CurrentPDU.DataSN;
         } else {
           if (TCB.SoFarInOrder = TRUE) {
               if (current DataSN is expected) {
                    Increment TCB.ExpectedDataSN.
               } else {

                    TCB.SoFarInOrder = FALSE;
                    send-data-SNACK = TRUE;
                   }
           } else {
                  if (current DataSN was considered missing) {
                      remove current DataSN from missing PDU list.
                  } else if (current DataSN is higher than expected)
{
                        send-data-SNACK = TRUE;
                   } else {
                         discard, return;
                   }
                   Adjust TCB.ExpectedDataSN if appropriate.
           }
           LastRequiredDataSN = CurrentPDU.DataSN - 1;
        }
        if (send-data-SNACK is TRUE and
               task is not already considered failed) {
           Recover-Data-if-Possible(LastRequiredDataSN, TCB);
     }
        if (missing data PDU list is empty) {
           TCB.SoFarInOrder = TRUE;
        }
     if (CurrentPDU.type == R2T) {
        Increment ActiveR2Ts for this task.

        Create a data-descriptor for the data burst.
        Build-And-Send-A-Data-Burst(Connection, data-descriptor,

                                                TCB);
     }
  } else if (CurrentPDU.type == Response) {
     if (Data-Digest-Bad) {
           send-status-SNACK = TRUE;
        } else {
        TCB.StatusXferd = TRUE;
        Store the status information in TCB.

        if (ExpDataSN does not match) {
             TCB.SoFarInOrder = FALSE;
             Recover-Data-if-Possible(current DataSN, TCB);
        }
           if (missing data PDU list is empty) {
                TCB.SoFarInOrder = TRUE;
           }
     }
  } else { /* REST UNRELATED TO WITHIN-COMMAND-RECOVERY, NOT
              SHOWN */
  }
  if ((TCB.SoFarInOrder == TRUE) and
                        (TCB.StatusXferd == TRUE)) {
     SCSI-Task-Completion(TCB);
  }
}

E.2.3. Target Algorithms โœจ

And now the target side, where it's all about spotting gaps in the data and sending R2Ts to fill them, periodt ๐Ÿฆ‹.

Receive-a-In-PDU(Connection, CurrentPDU)
{
  check-basic-validity(CurrentPDU);
  if (Header-Digest-Bad) discard, return;
  Retrieve TCB for CurrentPDU.InitiatorTaskTag.
  if (CurrentPDU.type == Data) {
      Retrieve TContext from CurrentPDU.TargetTransferTag;
      if (Data-Digest-Bad) {
            Build-And-Send-Reject(Connection, CurrentPDU,
                              Payload-Digest-Error);
         Note the missing data PDUs in MissingDataRange[].
            send-recovery-R2T = TRUE;
         } else {
         if (current DataSN is not expected) {
             Note the missing data PDUs in MissingDataRange[].
                send-recovery-R2T = TRUE;
            }
         if (CurrentPDU.Fbit == TRUE) {
             if (current PDU is solicited) {
                    Decrement TCB.ActiveR2Ts.
             }
             if ((current PDU is unsolicited and
                    data received is less than I/O length and
                      data received is less than FirstBurstLength)
                 or (current PDU is solicited and the length of
                      this burst is less than expected)) {
                 send-recovery-R2T = TRUE;
                 Note the missing data in MissingDataRange[].
             }

            }
         }
         Increment TContext.ExpectedDataSN.
      if (send-recovery-R2T is TRUE  and
                task is not already considered failed) {
         if (operational ErrorRecoveryLevel > 0) {
             Increment TCB.ActiveR2Ts.
             Create a data-descriptor for the data burst
                        from MissingDataRange.
             Build-And-Send-R2T(Connection, data-descriptor, TCB);
         } else {
              if (current PDU is the last unsolicited)
                 TCB.Reason = "Not enough unsolicited data";
              else
                  TCB.Reason = "Protocol service CRC error";
         }
      }
      if (TCB.ActiveR2Ts == 0) {
         Build-And-Send-Status(Connection, TCB);
      }
  } else if (CurrentPDU.type == SNACK) {
      snack-failure = FALSE;
      if (operational ErrorRecoveryLevel > 0) {
         if (CurrentPDU.type == Data/R2T) {
              if (the request is satisfiable) {

                 if (request for Data) {
                    Create a data-descriptor for the data burst
                        from BegRun and RunLength.
                    Build-And-Send-A-Data-Burst(Connection,

                                  data-descriptor, TCB);
                 } else { /* R2T */
                    Create a data-descriptor for the data burst
                        from BegRun and RunLength.
                    Build-And-Send-R2T(Connection, data-descriptor,
                                   TCB);
                  }
              } else {
                    snack-failure = TRUE;
              }
         } else if (CurrentPDU.type == status) {
              Handle-Status-SNACK-request(Connection, CurrentPDU);
         } else if (CurrentPDU.type == DataACK) {
              Consider all data upto CurrentPDU.BegRun as
              acknowledged.
              Free up the retransmission resources for that data.
         } else if (CurrentPDU.type == R-Data SNACK) {

                 Create a data descriptor for a data burst covering
                 all unacknowledged data.
              Build-And-Send-A-Data-Burst(Connection,
                                  data-descriptor, TCB);
              TCB.SNACK_Tag = CurrentPDU.SNACK_Tag;
              if (there's no more data to send) {
                 Build-And-Send-Status(Connection, TCB);
              }
         }
      } else { /* operational ErrorRecoveryLevel = 0 */
              snack-failure = TRUE;

      }
      if (snack-failure == TRUE) {
          Build-And-Send-Reject(Connection, CurrentPDU,
                                                  SNACK-Reject);
          if (TCB.StatusXferd != TRUE) {
              TCB.Reason = "SNACK Rejected";
              Build-And-Send-Status(Connection, TCB);
          }
      }

  } else { /* REST UNRELATED TO WITHIN-COMMAND-RECOVERY, NOT SHOWN */
  }
}

Transfer-Context-Timeout-Handler(TContext)
{
  Retrieve TCB and Connection from TContext.
  Decrement TCB.ActiveR2Ts.
  if (operational ErrorRecoveryLevel > 0 and
                task is not already considered failed) {
      Note the missing data PDUs in MissingDataRange[].
      Create a data-descriptor for the data burst
                        from MissingDataRange[].
      Build-And-Send-R2T(Connection, data-descriptor, TCB);
  } else {
      TCB.Reason = "Protocol service CRC error";
      if (TCB.ActiveR2Ts = 0) {
         Build-And-Send-Status(Connection, TCB);
      }
  }
}

E.3. Within-connection Recovery Algorithms โœจ

Okay, now we're zooming out to recovery within the whole connection, not just one command. Main character energy ๐Ÿ‘‘.

E.3.1. Procedure Descriptions

Procedure descriptions:
Recover-Status-if-Possible(transport connection,
                                    currently received PDU);
Evaluate-a-StatSN(transport connection, currently received PDU);
Retransmit-Command-if-Possible(transport connection, CmdSN);
Build-And-Send-SSnack(transport connection);
Build-And-Send-Command(transport connection, task control block);
Command-Acknowledge-Timeout-Handler(task control block);
Status-Expect-Timeout-Handler(transport connection);
Build-And-Send-Nop-Out(transport connection);
Handle-Status-SNACK-request(transport connection, status SNACK
PDU);
Retransmit-Status-Burst(status SNACK, task control block);
Is-Acknowledged(beginning StatSN, run length);

Implementation-specific tunables:
InitiatorProactiveSNACKEnabled

Notes:

  • The initiator algorithms only deal with unsolicited Nop-In PDUs for generating status SNACKs. A solicited Nop-In PDU has an assigned StatSN, which, when out of order, could trigger the out of order StatSN handling in Within-command algorithms, again leading to Recover-Status-if-Possible. ๐ŸŒธ
  • The pseudo-code shown may result in the retransmission of unacknowledged commands in more cases than necessary. This will not, however, affect the correctness of the operation because the target is required to discard the duplicate CmdSNs. ๐Ÿ™ˆ
  • The procedure Build-And-Send-Async is defined in the Connection recovery algorithms. ๐Ÿ“
  • The procedure Status-Expect-Timeout-Handler describes how initiators may proactively attempt to retrieve the Status if they so choose. This procedure is assumed to be triggered much before the standard ULP timeout. ๐Ÿงš

E.3.2. Initiator Algorithms

Bestie, the initiator is the one chasing down missing StatSNs here ๐Ÿ’–.

Recover-Status-if-Possible(Connection, CurrentPDU)
{
  if ((Connection.state == LOGGED_IN) and
                 connection is not already considered failed) {
     if (operational ErrorRecoveryLevel > 0) {
        if (# of missing PDUs is trackable) {
              Note the missing StatSNs in Connection
             that were not already requested with SNACK;
          Build-And-Send-SSnack(Connection);
            } else {
              Connection.PerformConnectionCleanup = TRUE;
        }
     } else {
            Connection.PerformConnectionCleanup = TRUE;
     }
     if (Connection.PerformConnectionCleanup == TRUE) {
        Start-Timer(Connection-Cleanup-Handler, Connection, 0);
         }
  }
}

Retransmit-Command-if-Possible(Connection, CmdSN)
{

  if (operational ErrorRecoveryLevel > 0) {
     Retrieve the InitiatorTaskTag, and thus TCB for the CmdSN.
     Build-And-Send-Command(Connection, TCB);
  }
}

Evaluate-a-StatSN(Connection, CurrentPDU)
{
  send-status-SNACK = FALSE;
  if (Connection.SoFarInOrder == TRUE) {
     if (current StatSN is the expected) {
          Increment Connection.ExpectedStatSN.
     } else {
              Connection.SoFarInOrder = FALSE;
              send-status-SNACK = TRUE;
         }
  } else {
     if (current StatSN was considered missing) {
          remove current StatSN from the missing list.
     } else {
              if (current StatSN is higher than expected){
                  send-status-SNACK = TRUE;

              } else {
                  send-status-SNACK = FALSE;
              discard the PDU;
          }
     }
     Adjust Connection.ExpectedStatSN if appropriate.
     if (missing StatSN list is empty) {
          Connection.SoFarInOrder = TRUE;
         }
  }
  return send-status-SNACK;
}

Receive-a-In-PDU(Connection, CurrentPDU)
{
  check-basic-validity(CurrentPDU);
  if (Header-Digest-Bad) discard, return;
  Retrieve TCB for CurrentPDU.InitiatorTaskTag.
  if (CurrentPDU.type == Nop-In) {
        if (the PDU is unsolicited) {
              if (current StatSN is not expected) {
                   Recover-Status-if-Possible(Connection,
                                CurrentPDU);
              }
              if (current ExpCmdSN is not Session.CmdSN) {
                  Retransmit-Command-if-Possible(Connection,
                                CurrentPDU.ExpCmdSN);
              }
        }
  } else if (CurrentPDU.type == Reject) {
        if (it is a data digest error on immediate data) {
              Retransmit-Command-if-Possible(Connection,
                                 CurrentPDU.BadPDUHeader.CmdSN);
        }
  } else if (CurrentPDU.type == Response) {
       send-status-SNACK = Evaluate-a-StatSN(Connection,
                                      CurrentPDU);
       if (send-status-SNACK == TRUE)
           Recover-Status-if-Possible(Connection, CurrentPDU);
  } else { /* REST UNRELATED TO WITHIN-CONNECTION-RECOVERY,
            * NOT SHOWN */
  }
}

Command-Acknowledge-Timeout-Handler(TCB)
{
  Retrieve the Connection for TCB.
  Retransmit-Command-if-Possible(Connection, TCB.CmdSN);

}

Status-Expect-Timeout-Handler(Connection)
{
  if (operational ErrorRecoveryLevel > 0) {
      Build-And-Send-Nop-Out(Connection);
  } else if (InitiatorProactiveSNACKEnabled){
      if ((Connection.state == LOGGED_IN) and
             connection is not already considered failed) {
           Build-And-Send-SSnack(Connection);
      }
  }
}

E.3.3. Target Algorithms ๐ŸŽฏ

Okay, now we flip over to the target side, bestie ๐Ÿ’… This is where the SNACK requests get handled.

Handle-Status-SNACK-request(Connection, CurrentPDU)
{
  if (operational ErrorRecoveryLevel > 0) {
     if (request for an acknowledged run) {
         Build-And-Send-Reject(Connection, CurrentPDU,
                                           Protocol-Error);
     } else if (request for an untransmitted run) {
         discard, return;
     } else {
         Retransmit-Status-Burst(CurrentPDU, TCB);
     } else {
        Build-And-Send-Async(Connection, DroppedConnection,
                                DefaultTime2Wait,
                                DefaultTime2Retain);
  }
}

E.4. Connection Recovery Algorithms ๐ŸŒธ

Now we're in the connection recovery zone, and it's giving main character energy โœจ

E.4.1. Procedure Descriptions ๐Ÿ“‹

Here's the full lineup of procedures, so you can see all the pieces at once ๐Ÿงš

Build-And-Send-Async(transport connection, reason code,
                                   minimum time, maximum time);
Pick-A-Logged-In-Connection(session);
Build-And-Send-Logout(transport connection, logout connection
                  identifier, reason code);
PerformImplicitLogout(transport connection, logout connection
                  identifier, target information);
PerformLogin(transport connection, target information);
CreateNewTransportConnection(target information);
Build-And-Send-Command(transport connection, task control block);
Connection-Cleanup-Handler(transport connection);
Connection-Resource-Timeout-Handler(transport connection);
Quiesce-And-Prepare-for-New-Allegiance(session, task control
block);
Build-And-Send-Logout-Response(transport connection,
                         CID of connection in recovery, reason
code);
Build-And-Send-TaskMgmt-Response(transport connection,
                       task mgmt command PDU, response code);
Establish-New-Allegiance(task control block, transport
connection);
Schedule-Command-To-Continue(task control block);

Notes:

  • Okay so transport drama ๐Ÿ’€ like an unexpected connection termination, a connection reset, or a hung connection while you're in the full-feature phase? Those are all assumed to get asynchronously signaled up to the iSCSI layer using the Transport_Exception_Handler procedure ๐Ÿซถ

E.4.2. Initiator Algorithms ๐Ÿ”ฅ

This is the initiator side of recovery, and honestly the logic is so iconic ๐Ÿ‘‘

         Receive-a-In-PDU(Connection, CurrentPDU) {
           check-basic-validity(CurrentPDU);
           if (Header-Digest-Bad) discard, return;

           Retrieve TCB from CurrentPDU.InitiatorTaskTag.
           if (CurrentPDU.type == Async) {
               if (CurrentPDU.AsyncEvent == ConnectionDropped) {
                  Retrieve the AffectedConnection for
         CurrentPDU.Parameter1.
                  AffectedConnection.CurrentTimeout =
         CurrentPDU.Parameter3;
                  AffectedConnection.State = CLEANUP_WAIT;
                  Start-Timer(Connection-Cleanup-Handler,
                               AffectedConnection,
         CurrentPDU.Parameter2);
               } else if (CurrentPDU.AsyncEvent == LogoutRequest)) {
                 AffectedConnection = Connection;
                 AffectedConnection.State = LOGOUT_REQUESTED;
                 AffectedConnection.PerformConnectionCleanup = TRUE;
                 AffectedConnection.CurrentTimeout =
         CurrentPDU.Parameter3;
                 Start-Timer(Connection-Cleanup-Handler,
                               AffectedConnection, 0);
               } else if (CurrentPDU.AsyncEvent == SessionDropped)) {
                 for (each Connection) {
                     Connection.State = CLEANUP_WAIT;
                     Connection.CurrentTimeout = CurrentPDU.Parameter3;
                     Start-Timer(Connection-Cleanup-Handler,
                               Connection, CurrentPDU.Parameter2);
                 }
                 Session.state = FAILED;
               }

           } else if (CurrentPDU.type == LogoutResponse) {
               Retrieve the CleanupConnection for CurrentPDU.CID.
               if (CurrentPDU.Response = failure) {
                  CleanupConnection.State = CLEANUP_WAIT;
               } else {
                   CleanupConnection.State = FREE;
               }
           } else if (CurrentPDU.type == LoginResponse) {
                if (this is a response to an implicit Logout) {
                   Retrieve the CleanupConnection.
                   if (successful) {
                       CleanupConnection.State = FREE;
                       Connection.State = LOGGED_IN;
                   } else {
                        CleanupConnection.State = CLEANUP_WAIT;
                        DestroyTransportConnection(Connection);
                   }
                }
           } else { /* REST UNRELATED TO CONNECTION-RECOVERY,

                     * NOT SHOWN */
           }
           if (CleanupConnection.State == FREE) {
              for (each command that was active on CleanupConnection) {
              /* Establish new connection allegiance */
                   NewConnection = Pick-A-Logged-In-Connection(Session);
                   Build-And-Send-Command(NewConnection, TCB);
               }
           } }

         Connection-Cleanup-Handler(Connection) {
           Retrieve Session from Connection.
           if (Connection can still exchange iSCSI PDUs) {
               NewConnection = Connection;
           } else {
               Start-Timer(Connection-Resource-Timeout-Handler,
                     Connection, Connection.CurrentTimeout);
               if (there are other logged-in connections) {
                    NewConnection = Pick-A-Logged-In-
         Connection(Session);
               } else {
                    NewConnection =

                      CreateTransportConnection(Session.OtherEndInfo);
                    Initiate an implicit Logout on NewConnection for
                                                      Connection.CID.
                    return;
               }
           }
           Build-And-Send-Logout(NewConnection, Connection.CID,
                                               RecoveryRemove); }

         Transport_Exception_Handler(Connection) {
           Connection.PerformConnectionCleanup = TRUE;
           if (the event is an unexpected transport disconnect) {
               Connection.State = CLEANUP_WAIT;

               Connection.CurrentTimeout = DefaultTime2Retain;
               Start-Timer(Connection-Cleanup-Handler, Connection,
                                                 DefaultTime2Wait);

           } else {
               Connection.State = FREE;
           } }

E.4.3. Target Algorithms โœจ

okay bestie, this is the target side of the connection-recovery story ๐Ÿฆ‹ when a Logout or Login shows up with recovery stuff, or a TaskReassign request comes in, the target has to sort out which TCB belongs to which connection. it's giving careful housekeeping, so read it slowly ๐Ÿ’…

Receive-a-In-PDU(Connection, CurrentPDU)
{
  check-basic-validity(CurrentPDU);
  if (Header-Digest-Bad) discard, return;
  else if (Data-Digest-Bad) {
        Build-And-Send-Reject(Connection, CurrentPDU,
                                    Payload-Digest-Error);
        discard, return;
  }
  Retrieve TCB and Session.
  if (CurrentPDU.type == Logout) {
     if (CurrentPDU.ReasonCode = RecoveryRemove) {
         Retrieve the CleanupConnection from CurrentPDU.CID).
         for (each command active on CleanupConnection) {
              Quiesce-And-Prepare-for-New-Allegiance(Session,
                                  TCB);
              TCB.CurrentlyAllegiant = FALSE;
         }
         Cleanup-Connection-State(CleanupConnection);
         if ((quiescing successful) and (cleanup successful)) {
              Build-And-Send-Logout-Response(Connection,
                               CleanupConnection.CID, Success);
         } else {
              Build-And-Send-Logout-Response(Connection,

                               CleanupConnection.CID, Failure);
         }
     }
  } else if ((CurrentPDU.type == Login) and
                     operational ErrorRecoveryLevel == 2) {
         Retrieve the CleanupConnection from CurrentPDU.CID).
         for (each command active on CleanupConnection) {
          Quiesce-And-Prepare-for-New-Allegiance(Session, TCB);
              TCB.CurrentlyAllegiant = FALSE;
         }
         Cleanup-Connection-State(CleanupConnection);
         if ((quiescing successful) and (cleanup successful)) {
              Continue with the rest of the Login processing;
         } else {
              Build-And-Send-Login-Response(Connection,
                         CleanupConnection.CID, Target Error);
         }
     }

  } else if (CurrentPDU.type == TaskManagement) {
       if (CurrentPDU.function == "TaskReassign") {
             if (Session.ErrorRecoveryLevel < 2) {
                Build-And-Send-TaskMgmt-Response(Connection,
                     CurrentPDU, "Allegiance reassignment
                                            not supported");
             } else if (task is not found) {
                Build-And-Send-TaskMgmt-Response(Connection,
                     CurrentPDU, "Task not in task set");
             } else if (task is currently allegiant) {
                Build-And-Send-TaskMgmt-Response(Connection,
                          CurrentPDU, "Task still allegiant");
             } else {
                Establish-New-Allegiance(TCB, Connection);
                TCB.CurrentlyAllegiant = TRUE;
                Schedule-Command-To-Continue(TCB);
             }
       }
  } else { /* REST UNRELATED TO CONNECTION-RECOVERY,
            * NOT SHOWN */
  }
}

Transport_Exception_Handler(Connection)
{
  Connection.PerformConnectionCleanup = TRUE;
  if (the event is an unexpected transport disconnect) {
      Connection.State = CLEANUP_WAIT;
      Start-Timer(Connection-Resource-Timeout-Handler,
      Connection,

(DefaultTime2Wait+DefaultTime2Retain));
        if (this Session has full-feature phase connections
             left)
{
          DifferentConnection =
             Pick-A-Logged-In-Connection(Session);
           Build-And-Send-Async(DifferentConnection,
                 DroppedConnection, DefaultTime2Wait,
                   DefaultTime2Retain);
     }
  } else {
      Connection.State = FREE;
  }
}

Appendix F. Clearing Effects of Various Events on Targets ๐ŸŒธ

F.1. Clearing Effects on iSCSI Objects โœจ

okay so these tables tell the target what to clear, discard, or reset when stuff happens ๐Ÿงน the second table extends the first one with more objects for the same events ๐Ÿ’… here's the legend:

  • Y = Yes (cleared/discarded/reset on the event in that row). Unless otherwise noted, the clearing action only applies to the issuing initiator port ๐ŸŽ€
  • N = No (not affected by the event in that row, so it stays at its previous value) ๐Ÿฆ‹
  • NA = Not Applicable or Not Defined ๐Ÿ’€
                         +-----+-----+-----+-----+-----+
                         |IT(1)|IC(2)|CT(5)|ST(6)|PP(7)|
   +---------------------+-----+-----+-----+-----+-----+
   |connection failure(8)|Y    |Y    |N    |N    |Y    |
   +---------------------+-----+-----+-----+-----+-----+
   |connection state     |NA   |NA   |Y    |N    |NA   |
   |timeout (9)          |     |     |     |     |     |
   +---------------------+-----+-----+-----+-----+-----+
   |session timeout/     |Y    |Y    |Y    |Y    |Y(14)|
   |closure/reinstatement|     |     |     |     |     |
   |(10)                 |     |     |     |     |     |
   +---------------------+-----+-----+-----+-----+-----+
   |session continuation |NA   |NA   |N(11)|N    |NA   |
   |(12)                 |     |     |     |     |     |
   +---------------------+-----+-----+-----+-----+-----+
   |successful connection|Y    |Y    |Y    |N    |Y(13)|
   |close logout         |     |     |     |     |     |
   +---------------------+-----+-----+-----+-----+-----+
   |session failure (18) |Y    |Y    |N    |N    |Y    |
   +---------------------+-----+-----+-----+-----+-----+
   |successful recovery  |Y    |Y    |N    |N    |Y(13)|
   |Logout               |     |     |     |     |     |
   +---------------------+-----+-----+-----+-----+-----+
   |failed Logout        |Y    |Y    |N    |N    |Y    |
   +---------------------+-----+-----+-----+-----+-----+
   |connection Login     |NA   |NA   |NA   |Y(15)|NA   |
   |(leading)            |     |     |     |     |     |
   +---------------------+-----+-----+-----+-----+-----+
   |connection Login     |NA   |NA   |N(11)|N    |Y    |
   |(non-leading)        |     |     |     |     |     |
   +---------------------+-----+-----+-----+-----+-----+
   |target cold reset(16)|Y    |Y    |Y    |Y    |Y    |
   +---------------------+-----+-----+-----+-----+-----+
   |target warm reset(16)|Y    |Y    |Y    |Y    |Y    |
   +---------------------+-----+-----+-----+-----+-----+
   |LU reset(19)         |Y    |Y    |Y    |Y    |Y    |
   +---------------------+-----+-----+-----+-----+-----+
   |powercycle(16)       |Y    |Y    |Y    |Y    |Y    |
   +---------------------+-----+-----+-----+-----+-----+
  1. Incomplete TTTs - Target Transfer Tags on which the target is still expecting PDUs to be received. Examples include TTTs received via R2T, NOP-IN, etc. ๐Ÿ“
  2. Immediate Commands - immediate commands, but waiting for execution on a target. For example, Abort Task Set. ๐Ÿงš
  3. Connection Tasks - tasks that are active on the iSCSI connection in question. ๐ŸŒธ
  4. Session Tasks - tasks that are active on the entire iSCSI session. A union of "connection tasks" on all participating connections. ๐Ÿ‘‘
  5. Partial PDUs (if any) - PDUs that are partially sent and waiting for transport window credit to complete the transmission. ๐Ÿซถ
  6. Connection failure is a connection exception condition. One of the transport connections shut down, got reset, or timed out, which abruptly terminated the iSCSI full-feature phase connection. A connection failure always takes the connection state machine to the CLEANUP_WAIT state. ๐Ÿ˜ญ
  7. Connection state timeout happens if a connection spends more time than agreed upon during Login negotiation in the CLEANUP_WAIT state, and this takes the connection to the FREE state (M1 transition in connection cleanup state diagram). โฐ
  8. These are defined in Section 5.3.5 Session Reinstatement, Closure, and Timeout. ๐Ÿ“–
  9. This clearing effect is "Y" only if it is a connection reinstatement and the operational ErrorRecoveryLevel is less than 2. ๐Ÿ”ฅ
  10. Session continuation is defined in Section 5.3.6 Session Continuation and Failure. ๐Ÿ“–
  11. This clearing effect is only valid if the connection is being logged out on a different connection and when the connection being logged out on the target may have some partial PDUs pending to be sent. In all other cases, the effect is "NA". ๐Ÿ’…
  12. This clearing effect is only valid for a "close the session" logout in a multi-connection session. In all other cases, the effect is "NA". ๐ŸŒˆ
  13. Only applicable if this leading connection login is a session reinstatement. If this is not the case, it is "NA". โœจ
  14. This operation affects all logged-in initiators. ๐ŸŒ
  15. Session failure is defined in Section 5.3.6 Session Continuation and Failure. ๐Ÿ“–
  16. This operation affects all logged-in initiators, and the clearing effects are only applicable to the LU being reset. ๐ŸŽฏ

Okay, now the second table, which is the extension I mentioned. Same legend, new columns ๐Ÿ’–

                         +-----+-----+-----+-----+-----+
                         |DC(1)|DD(2)|SS(3)|CS(4)|DS(5)|
   +---------------------+-----+-----+-----+-----+-----+
   |connection failure   |N    |Y    |N    |N    |N    |
   +---------------------+-----+-----+-----+-----+-----+
   |connection state     |Y    |NA   |Y    |N    |NA   |
   |timeout              |     |     |     |     |     |
   +---------------------+-----+-----+-----+-----+-----+
   |session timeout/     |Y    |Y    |Y(7) |Y    |NA   |
   |closure/reinstatement|     |     |     |     |     |
   +---------------------+-----+-----+-----+-----+-----+
   |session continuation |N(11)|NA*12|NA   |N    |NA*13|
   +---------------------+-----+-----+-----+-----+-----+
   |successful connection|Y    |Y    |Y    |N    |NA   |
   |close Logout         |     |     |     |     |     |
   +---------------------+-----+-----+-----+-----+-----+
   |session failure      |N    |Y    |N    |N    |N    |
   +---------------------+-----+-----+-----+-----+-----+
   |successful recovery  |Y    |Y    |Y    |N    |N    |
   |Logout               |     |     |     |     |     |
   +---------------------+-----+-----+-----+-----+-----+
   |failed Logout        |N    |Y(9) |N    |N    |N    |
   +---------------------+-----+-----+-----+-----+-----+
   |connection Login     |NA   |NA   |N(8) |N(8) |NA   |
   |(leading             |     |     |     |     |     |
   +---------------------+-----+-----+-----+-----+-----+
   |connection Login     |N(11)|NA*12|N(8) |N    |NA*13|
   |(non-leading)        |     |     |     |     |     |
   +---------------------+-----+-----+-----+-----+-----+
   |target cold reset    |Y    |Y    |Y    |Y(10)|NA   |
   +---------------------+-----+-----+-----+-----+-----+
   |target warm reset    |Y    |Y    |N    |N    |NA   |
   +---------------------+-----+-----+-----+-----+-----+
   |LU reset             |N    |Y    |N    |N    |N    |
   +---------------------+-----+-----+-----+-----+-----+
   |powercycle           |Y    |Y    |Y    |Y(10)|NA   |
   +---------------------+-----+-----+-----+-----+-----+
  1. Discontiguous Commands - commands allegiant to the connection in question and waiting to be reordered in the iSCSI layer. All "Y"s in this column assume that the task causing the event (if indeed the event is the result of a task) is issued as an immediate command, because the discontiguities can be ahead of the task. ๐Ÿงฉ
  2. Discontiguous Data - data PDUs received for the task in question and waiting to be reordered due to prior discontiguities in DataSN. ๐Ÿ“ฆ
  3. StatSN ๐Ÿ”ข
  4. CmdSN ๐Ÿ”ข
  5. DataSN ๐Ÿ”ข
  6. It clears the StatSN on all the connections. ๐Ÿงน
  7. This sequence number is instantiated on this event. โœจ
  8. A logout failure drives the connection state machine to the CLEANUP_WAIT state, similar to the connection failure event. Hence, it has a similar effect on this and several other protocol aspects. ๐Ÿ˜ตโ€๐Ÿ’ซ
  9. This is cleared by virtue of the fact that all sessions with all initiators are terminated. ๐Ÿšช
  10. This clearing effect is "Y" if it is a connection reinstatement. ๐Ÿ”
  11. This clearing effect is "Y" only if it is a connection reinstatement and the operational ErrorRecoveryLevel is 2. ๐Ÿ”
  12. This clearing effect is "N" only if it is a connection reinstatement and the operational ErrorRecoveryLevel is 2. ๐Ÿ”

F.2. Clearing Effects on SCSI Objects ๐ŸŽ€

The only iSCSI protocol action that can cause clearing actions on SCSI objects is the "I_T nexus loss" notification (Section 4.3.5.1 Loss of Nexus notification). [SPC3] describes what this notification clears across a bunch of SCSI attributes. On top of that, SCSI standards docs (like [SAM2] and [SBC]) define extra clearing actions that can happen to several SCSI objects on SCSI events such as LU resets and power-on resets ๐ŸŒŸ

Since iSCSI treats a target cold reset as protocol-equivalent to a target power-cycle, the iSCSI target cold reset also has to count as the power-on reset event when you read the actions in the SCSI standards โšก

When the iSCSI session gets rebuilt (between the same SCSI ports with the same nexus identifier), reestablishing the same I_T nexus, every SCSI object that is defined to not clear on the "I_T nexus loss" notification, like persistent reservations, is automatically associated with this new session ๐Ÿซถ๐Ÿ’œ

Acknowledgements ๐Ÿซถ

okay so this protocol was built by a design team! ๐Ÿ’… besides the authors, the team included Daniel Smith, Ofer Biran, Jim Hafner and John Hufferd (IBM), Mark Bakke (Cisco), Randy Haagens (HP), Matt Wakeley (Agilent, now Sierra Logic), Luciano Dalle Ore (Quantum), and Paul Von Stamwitz (Adaptec, now TrueSAN Networks). the whole team is iconic ๐Ÿ‘‘

a huge crowd also helped by reviewing, commenting, and sharing insights, and the authors are so grateful ๐Ÿฅน special shoutout to everyone who made time for the weekly phone calls and the in-person meetings in Almaden and Haifa, because those literally shaped this doc โœจ: Prasenjit Sarkar, Meir Toledano, John Dowdy, Steve Legg, Alain Azagury (IBM), Dave Nagle (CMU), David Black (EMC), John Matze (Veritas - now Okapi Software), Steve DeGroote, Mark Schrandt (Cisco), Gabi Hecht (Gadzoox), Robert Snively and Brian Forbes (Brocade), Nelson Nachum (StorAge), and Uri Elzur (Broadcom). ๐ŸŒธ

lots of other people edited and improved the document inside the IPS working group, and we're especially thankful to David Robinson and Raghavendra Rao (Sun), Charles Monia, Joshua Tseng (Nishan), Somesh Gupta (Silverback), Michael Krause, Pierre Labat, Santosh Rao, Matthew Burbridge, Bob Barry, Robert Elliott, Nick Martin (HP), Stephen Bailey (Sandburst), Steve Senum, Ayman Ghanem, Dave Peterson (Cisco), Barry Reinhold (Trebia Networks), Bob Russell (UNH), Eddy Quicksall (iVivity, Inc.), Bill Lynn and Michael Fischer (Adaptec), Vince Cavanna, Pat Thaler (Agilent), Jonathan Stone (Stanford), Luben Tuikov (Splentec), Paul Koning (EqualLogic), Michael Krueger (Windriver), Martins Krikis (Intel), Doug Otis (Sanlight), John Marberg (IBM), Robert Griswold and Bill Moody (Crossroads), Bill Studenmund (Wasabi Systems), Elizabeth Rodriguez (Brocade) and Yaron Klein (Sanrad). ๐Ÿ’–

the recovery chapter got a glow-up thanks to Stephen Bailey (Sandburst), Somesh Gupta (Silverback), and Venkat Rangan (Rhapsody Networks) ๐Ÿ”ฅ. Eddy Quicksall contributed some examples and kicked off the Definitions section, and Michael Fischer and Bob Barry started the Acronyms section. last but not least, Ralph Weber kept everyone in line with T10 (SCSI) standardization ๐Ÿซก

we also want to thank Steve Hetzler for his unwavering support and for coming up with such a good name for the protocol ๐Ÿงš, plus Micky Rodeh, Jai Menon, Clod Barrera, and Andy Bechtolsheim for helping make this work happen ๐ŸŒˆ

if you want the full picture of the iSCSI spec, the authors recommend checking out these: "iSCSI Naming & Discovery"[RFC3721], "Bootstrapping Clients using the iSCSI Protocol" [BOOT], "Securing Block Storage Protocols over IP" [RFC3723] documents, "iSCSI Requirements and Design Considerations" [RFC3347] and "SCSI Command Ordering Considerations with iSCSI" [CORD] ๐Ÿ“š

the "iSCSI Naming & Discovery" doc was written by:

  • Mark Bakke (Cisco), Jim Hafner, John Hufferd, Kaladhar Voruganti (IBM), and Marjorie Krueger (HP).

the "Bootstrapping Clients using the iSCSI Protocol" doc was written by:

  • Prasenjit Sarkar (IBM), Duncan Missimer (HP), and Costa Sapuntzakis (Cisco).

the "Securing Block Storage Protocols over IP" doc was written by:

  • Bernard Aboba (Microsoft), Joshua Tseng (Nishan), Jesse Walker (Intel), Venkat Rangan (Rhapsody Networks), and Franco Travostino (Nortel Networks).

the "iSCSI Requirements and Design Considerations" doc was written by:

  • Marjorie Krueger, Randy Haagens (HP), Costa Sapuntzakis, and Mark Bakke (Cisco).

the "SCSI Command Ordering Considerations with iSCSI" doc was written by:

  • Mallikarjun Chadalapaka, Rob Elliot (HP)

the authors are so grateful to all of them for their amazing work and for helping them line this document up with the ones they produced ๐Ÿฅฐ

Authors' Addresses ๐Ÿ“ฌ

Julian Satran
IBM Research Laboratory in Haifa
Haifa University Campus - Mount Carmel
Haifa 31905, Israel

Phone +972.4.829.6264
EMail: Julian_Satran@il.ibm.com


Kalman Meth
IBM Research Laboratory in Haifa
Haifa University Campus - Mount Carmel
Haifa 31905, Israel

Phone +972.4.829.6341
EMail: meth@il.ibm.com


Costa Sapuntzakis
Stanford University
353 Serra Mall Dr #407
Stanford, CA 94305

Phone: +1.650.723.2458
EMail: csapuntz@alum.mit.edu


Efri Zeidner
XIV Ltd.
1 Azrieli Center,
Tel-Aviv 67021, Israel

Phone: +972.3.607.4722
EMail: efri@xiv.co.il


Mallikarjun Chadalapaka
Hewlett-Packard Company
8000 Foothills Blvd.
Roseville, CA 95747-5668, USA

Phone: +1.916.785.5621
EMail: cbm@rose.hp.com

Copyright (C) The Internet Society (2004). This document is subject to the rights, licenses and restrictions contained in BCP 78, and except as set forth therein, the authors retain all their rights.

This document and the information contained herein are provided on an "AS IS" basis and THE CONTRIBUTOR, THE ORGANIZATION HE/SHE REPRESENTS OR IS SPONSORED BY (IF ANY), THE INTERNET SOCIETY AND THE INTERNET ENGINEERING TASK FORCE DISCLAIM ALL WARRANTIES, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO ANY WARRANTY THAT THE USE OF THE INFORMATION HEREIN WILL NOT INFRINGE ANY RIGHTS OR ANY IMPLIED WARRANTIES OF MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE.

Intellectual Property ๐Ÿงพ

The IETF takes no position regarding the validity or scope of any Intellectual Property Rights or other rights that might be claimed to pertain to the implementation or use of the technology described in this document or the extent to which any license under such rights might or might not be available; nor does it represent that it has made any independent effort to identify any such rights. Information on the procedures with respect to rights in RFC documents can be found in BCP 78 and BCP 79.

Copies of IPR disclosures made to the IETF Secretariat and any assurances of licenses to be made available, or the result of an attempt made to obtain a general license or permission for the use of such proprietary rights by implementers or users of this specification can be obtained from the IETF on-line IPR repository at http://www.ietf.org/ipr.

The IETF invites any interested party to bring to its attention any copyrights, patents or patent applications, or other proprietary rights that may cover technology that may be required to implement this standard. Please address the information to the IETF at ietf-ipr@ietf.org.

Acknowledgement ๐ŸŒท

Funding for the RFC Editor function is currently provided by the Internet Society.

๐Ÿ“ฆ where this page comes from